问题
I have this at the beginning of a class:
@Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2')
class MyClass{...
I'm trying to unit test this class, but whenever I try to run JUnit 4 tests, I get this error:
Caused by: java.lang.RuntimeException: No suitable ClassLoader found for grab
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:77)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:102)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:190)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:198)
at groovy.grape.GrapeIvy.chooseClassLoader(GrapeIvy.groovy:163)
at groovy.grape.GrapeIvy$chooseClassLoader.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:149)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:227)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSite.invoke(PogoMetaMethodSite.java:225)
at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:51)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:153)
at groovy.grape.GrapeIvy.grab(GrapeIvy.groovy:216)
at groovy.grape.Grape.grab(Grape.java:131)
at groovy.grape.Grape$grab.callStatic(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:165)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:173)
at ammoscanner.AmmoScanner.<clinit>(AmmoScanner.groovy)
... 30 more
Any ideas? I'm using groovy 1.7.5
回答1:
Using @Grab
makes code untestable, at least as of 01/26/2011.
回答2:
The Problem
Looking at the source code, this exception is thrown whenever the supplied ClassLoader's name (or it's superclasses) is not groovy.lang.GroovyClassLoader
or org.codehaus.groovy.tools.RootLoader
. i.e. The target classloader must be an instance of the aforementioned classes (a bit restrictive IMHO).
A Solution
Currently I don't know how to configure a specific classloader using @Grape
/@Grab
/@GrabConfig
annotations. The closest would be to use @GrabConfig(systemClassLoader=true)
, and ensure the System classloader is an instance of one of the above ClassLoader classes.
If anyone does know, please let me know (and I'll update this answer).
A Workaround
The following code will programmatically download your Grapes, and load them into the supplied GroovyClassLoader (admittedly, not quite what you want).
def loadGrapes(){
ClassLoader classLoader = new groovy.lang.GroovyClassLoader()
Map[] grapez = [[group : 'org.ccil.cowan.tagsoup', module : 'tagsoup', version : '1.2']]
Grape.grab(classLoader: classLoader, grapez)
println "Class: " + classLoader.loadClass('org.ccil.cowan.tagsoup.jaxp.SAXParserImpl')
}
回答3:
I assume you've tried adding
@GrabConfig(systemClassLoader=true)
like so:
@Grapes([
@Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2'),
@GrabConfig( systemClassLoader=true )
])
class MyClass{...
回答4:
If you are not using systemClassLoader=true then it seems your IDE is not rrunning the code with a groovy compiler, you can check that with a simple groovy class that outputs the class name of its classloader. I would guess it tries to compile the groovy classes and run them with a non-groovy classloader.
See also this answer to General error during conversion: No suitable ClassLoader found for grab. Also this blog post explains more about running pre-compiled groovy classes with the stock classloader.
回答5:
Add the plugin snapshot update site for Kepler.
This seems to solve the "..no suitable classloader problem". Unfortunately, I still had to add the grape repo to the classpath for the project after this.
回答6:
There's one more solution for testing a class with @Grab
annotation:
- Extract an interface from this class.
- Create another class which implements its interface. Move the
@Grab
annotation to this class. Then make this class a simple wrapper, which just passes all the messages to the original class. - Run the tests against your original class.
- Whenever you need to have a version
@Grab
, use the wrapper.
来源:https://stackoverflow.com/questions/4611230/no-suitable-classloader-found-for-grab