找不到合适的类加载器可供抓取

发布于 2024-10-10 17:43:59 字数 3032 浏览 5 评论 0原文

我在课程的开头有这样的内容:

@Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2')
class MyClass{...

我正在尝试对此类进行单元测试,但是每当我尝试运行 JUnit 4 测试时,我都会收到此错误:

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

有什么想法吗?我正在使用 groovy 1.7.5

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

二智少女猫性小仙女 2024-10-17 17:43:59

问题

查看源代码,只要提供的 ClassLoader 的名称(或其超类)不是 groovy.lang.GroovyClassLoaderorg.codehaus.groovy.tools.RootLoader ,就会引发此异常代码>.即目标类加载器必须是上述类的实例(恕我直言,有点限制)。

解决方案

目前我不知道如何使用 @Grape/@Grab/@GrabConfig 注解配置特定的类加载器。最接近的方法是使用@GrabConfig(systemClassLoader=true),并确保系统类加载器是上述 ClassLoader 类之一的实例。

如果有人知道,请告诉我(我会更新这个答案)。

解决方法

以下代码将以编程方式下载您的 Grapes,并将它们加载到提供的 GroovyClassLoader (诚然,这不完全是你想要的)。

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')
}

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')
}
心意如水 2024-10-17 17:43:59

使用 @Grab 会使代码无法测试,至少截至 2011 年 1 月 26 日是这样。

Using @Grab makes code untestable, at least as of 01/26/2011.

心房敞 2024-10-17 17:43:59

对我有用的解决方案(都用于在 IntelliJ 中使用 @Grab 和通过 Maven 运行脚本测试):

  1. 在 Maven pom 中引用通过 @Grab 使用的依赖项.xml 文件(无论如何,这对于更好的编码体验很有用):

例如,我的 Groovy 脚本中有以下 @Grab

@Grab(group='info.picocli', module='picocli', version='4.6.1')

因此我添加以下 Maven 依赖项:

    <dependency>
      <groupId>info.picocli</groupId>
      <artifactId>picocli</artifactId>
      <version>4.6.1</version>
    </dependency>
  1. 添加可选依赖项Maven pom.xml 文件中的 ivy (需要在 IDE 中正确处理 @Grab):
    <dependency>
      <groupId>org.apache.ivy</groupId>
      <artifactId>ivy</artifactId>
      <version>${ivy.version}</version>
      <scope>compile</scope>
      <optional>true</optional>
    </dependency>
  1. 在测试代码中,设置 groovy.grape.enable 系统属性设置为 false这是解决方案的主要和关键部分 - 它禁用脚本的 @Grab 注释处理,但请记住我们已经在 Maven pom 中引用了这些依赖项.xml 文件:
    static {
        System.setProperty("groovy.grape.enable", "false")
    }

    @Test
    void test() {
        MainScript.call()
    }

该解决方案的缺点是您必须在 @Grab 和 Maven pom.xml 文件中复制依赖项,但同样,如果您开发您经常这样做的 Groovy 脚本是为了改善您的编码体验(获得更好的代码突出显示等)。

Solution that worked for me (both for running tests for scripts using @Grab in IntelliJ and via Maven):

  1. Reference the dependencies used via @Grab in your Maven pom.xml file (this is useful anyway for better coding experience):

E.g. I have the following @Grab in my Groovy script:

@Grab(group='info.picocli', module='picocli', version='4.6.1')

So I add the following Maven dependency:

    <dependency>
      <groupId>info.picocli</groupId>
      <artifactId>picocli</artifactId>
      <version>4.6.1</version>
    </dependency>
  1. Add an optional dependency on ivy in your Maven pom.xml file (needed to handle @Grab properly in your IDE):
    <dependency>
      <groupId>org.apache.ivy</groupId>
      <artifactId>ivy</artifactId>
      <version>${ivy.version}</version>
      <scope>compile</scope>
      <optional>true</optional>
    </dependency>
  1. In your test code, set groovy.grape.enable system property to false. This is the main and crucial part of the solution - it disables @Grab annotation processing for the script, but remember we already have those dependencies referenced in our Maven pom.xml file:
    static {
        System.setProperty("groovy.grape.enable", "false")
    }

    @Test
    void test() {
        MainScript.call()
    }

The downside of the solution is that you have to duplicate your dependencies in @Grab and Maven pom.xml file but again, if you develop a Groovy script you oftentimes already do so to improve your coding experience (get better code highlights etc).

泪之魂 2024-10-17 17:43:59

我假设您尝试过

@GrabConfig(systemClassLoader=true)

像这样添加:

@Grapes([
    @Grab(group = 'org.ccil.cowan.tagsoup', module = 'tagsoup', version = '1.2'),
    @GrabConfig( systemClassLoader=true )
])
class MyClass{...

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{...
故事灯 2024-10-17 17:43:59

如果您没有使用 systemClassLoader=true ,那么您的 IDE 似乎没有使用 groovy 编译器运行代码,您可以使用一个简单的 groovy 类来检查,该类输出其类加载器的类名。我猜它会尝试编译 groovy 类并使用非 groovy 类加载器运行它们。

另请参阅此答案对转换期间的常规错误:找不到合适的类加载器供抓取。另外这篇博文解释了有关跑步的更多信息使用库存类加载器预编译 Groovy 类。

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.

凶凌 2024-10-17 17:43:59

添加 Kepler 的插件快照更新站点

这似乎解决了“..没有合适的类加载器问题”。不幸的是,在此之后我仍然必须将葡萄存储库添加到项目的类路径中。

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.

带上头具痛哭 2024-10-17 17:43:59

还有一种使用 @Grab 注释测试类的解决方案:

  1. 从此类中提取接口。
  2. 创建另一个实现其接口的类。将 @Grab 注释移至此类。然后让这个类成为一个简单的包装器,它只是将所有消息传递给原始类。
  3. 针对您的原始班级运行测试。
  4. 每当您需要一个版本@Grab时,请使用包装器。

There's one more solution for testing a class with @Grab annotation:

  1. Extract an interface from this class.
  2. 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.
  3. Run the tests against your original class.
  4. Whenever you need to have a version @Grab, use the wrapper.
栩栩如生 2024-10-17 17:43:59

这个问题有一个解决办法!

您可以使用 Groovy 的元编程来重写负责确定类加载器是 groovy.lang.GroovyClassLoader 的实例还是 org.codehaus.groovy.tools.RootLoader 的实例的方法。

由于 这个 Groovy 错误,您无法使用元编程覆盖私有方法,否则您可以继续通过执行以下操作来更改 isValidTargetClassLoaderClass 方法:

GrapeIvy.metaClass.isValidTargetClassLoaderClass = { Class loaderClass ->
    return (loaderClass != null)
}

但是,isValidTargetClassLoaderClass 是由 isValidTargetClassLoader (另一个私有方法)调用的,该方法称为通过 chooseClassLoader,这是一个 public 方法,可以使用元编程重写:

GrapeIvy.metaClass.chooseClassLoader = { Map args ->
    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) {
            throw new RuntimeException("No suitable ClassLoader found for grab")
        }
    }
    return loader
}

我所做的就是替换对 !isValidTargetClassLoader 的任何调用loader?.class == null

现在,我正在使用 Spock,因此我将此代码放入测试类的 setupSpec 方法中。但是,如果您使用 JUnit,我想它会想要进入用 @BeforeClass 注释的方法。

下面是它与 Spock 一起工作的示例(注意 IntelliJ 显示它将返回一个通常会抛出异常的类加载器:

在此处输入图像描述

There is a solution to this!

You can use Groovy's metaprogramming to override the methods responsible for determining if the class loader is an instance of groovy.lang.GroovyClassLoader or org.codehaus.groovy.tools.RootLoader.

Because of this Groovy bug, you cannot override the private methods using metaprogramming, otherwise you could go ahead and change the isValidTargetClassLoaderClass method by doing this:

GrapeIvy.metaClass.isValidTargetClassLoaderClass = { Class loaderClass ->
    return (loaderClass != null)
}

However, isValidTargetClassLoaderClass is called by isValidTargetClassLoader (another private method), which is called by chooseClassLoader, which is a public method, which can be overridden using metaprogramming:

GrapeIvy.metaClass.chooseClassLoader = { Map args ->
    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) {
            throw new RuntimeException("No suitable ClassLoader found for grab")
        }
    }
    return loader
}

All I did was replace any calls to !isValidTargetClassLoader with loader?.class == null.

Now, I am using Spock, so I put this code in my setupSpec method in my test class. However if you are using JUnit, I would imagine it would want to go in the method annotated with @BeforeClass.

Here is an example of it working with Spock (notice IntelliJ showing it about to return a class loader that would normally throw an exception:

enter image description here

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文