我有一个 Groovy 库作为全局共享库提供:
package com.example
@Grab(group="org.apache.httpcomponents", module="httpclient", version="[4.5.3,)")
import org.apache.http.HttpHost
import org.apache.http.impl.client.HttpClients
class MyClass implements Serializable {
static def run() {
return HttpClients.custom()
.setProxy(new HttpHost("proxy.example.com", 3128))
.build()
}
static def debug() {
return ("""
this: ${this.classLoader.class.toString()} ${this.classLoader.hashCode().toString()}
HttpHost: ${HttpHost.class.classLoader.class.toString()} ${HttpHost.class.classLoader.hashCode()}
HttpClients: ${HttpClients.class.classLoader.class.toString()} ${HttpClients.class.classLoader.hashCode()}
""")
}
}
以及使用此库的 Jenkins 脚本化管道作业:
@Library('example') _
node {
echo "${com.example.MyClass.debug()}"
com.example.MyClass.run()
}
当作业运行时,我得到以下输出debug()
,随后出现错误run()
:
this: class org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$CleanGroovyClassLoader 765101363
HttpHost: class hudson.ClassicPluginStrategy$AntClassLoader2 804623541
HttpClients: class hudson.ClassicPluginStrategy$AntClassLoader2 1870591909
hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: org.apache.http.impl.client.HttpClientBuilder.setProxy() is applicable for argument types: (org.apache.http.HttpHost) values: [http://proxy.example.com:3128]
Possible solutions: setProxy(org.apache.http.HttpHost)
The following classes appear as argument class and as parameter class, but are defined by different class loader
我很清楚一些 Jenkins 插件已经依赖于 httpcomponents,并且以下情况似乎是正确的:
- My
@Grab
注释导致下载请求的 httpclient 版本(如~/.groovy/grapes
).
- 但是,该版本并未被 Groovy 库加载或使用,而是由某些 Jenkins 插件依赖的其他版本加载或使用。
- 而且,更令人恼火的是,
HttpHost
and HttpClients
正在从不同的类加载器加载,因此我什至无法使用泄漏到我的 Groovy 代码的类加载器中的插件版本。
Versions
插件版本
- 格罗维:2.0
- 管道:2.5
- 管道:Groovy:2.30
- 管道:共享 Groovy 库:2.8
有没有办法在与 Jenkins 插件隔离的类加载器中运行我的 Groovy? Jenkins 和 Groovy 共享库代码如何组织类加载器?插件引入的类的泄漏是故意的吗?
这是一个错误还是我做错了什么?我意识到我在 Jenkins 上落后了几个版本,所以这是值得尝试的一件事。
事实上,系统无法使用,除非我足够幸运拥有其他插件没有的依赖项,或者幸运地与类加载器碰巧找到的任何版本兼容。