Groovy 扩展模块方法 - 没有方法签名
Groovy Extension Module Method - No Signature of Method
我在 java.util.ArrayList()
上创建了两个 groovy 扩展模块/方法。它在我的 IDE 中运行良好。我使用 gradle
构建 jar,并将其部署到远程 JVM
。当它到达远程 JVM
时,它失败了。
这里是扩展方法:
static Map sumSelectedAttributes(final List self, List attributes) {
Map resultsMap = [:]
attributes.each { attr ->
resultsMap[attr] = self.inject(0) { sum, obj ->
sum + obj[attr]
}
}
return resultsMap
这是调用它的代码:
outputMap[processName][iName] << kBeanList.sumSelectedAttributes([
"messageCount", "userCount", "outstandingRequests",
"cpuUsage", "memoryUsage", "threadCount", "cacheCount", "huserCount",
"manualHuserCount", "dataPointerCount", "tableHandleCount",
"jdbCacheRecordCount", "dbConnectionCount"])
这是错误:
No signature of method: java.util.ArrayList.sumSelectedAttributes() is
applicable for argument types: (java.util.ArrayList) values:
[[messageCount, incomingConnectionsCount, outgoingConnectionsCount,
...]]
同样,它在带有测试用例的 intellij 中运行良好。远程 JVM 上有什么不同会阻止它工作?以下是我想到的一些事情:
- 远程 JVM 使用 Groovy 2.3 而我在 2.4.5
- 我们在远程 JVM 上使用自定义 classloader 来加载 classes
除此之外,我找不到任何其他文档来说明我需要做什么才能使扩展在远程 JVM 上工作。
非常感谢任何帮助。
根据评论,似乎是自定义 classloader 的问题,这里是处理一些 classloader 操作的 class。
class CustomLoader {
static Map loaders = [:]
static File loaderRoot = new File("../branches")
static URLClassLoader getCustomLoader(String branchName) {
if (!loaders[branchName]) {
loaders[branchName] = new URLClassLoader(getUrls(branchName))
} else {
loaders[branchName]
}
}
static URLClassLoader updateClassLoader(String branchName) {
loaders[branchName] = null
loaders[branchName] = new URLClassLoader(getUrls(branchName))
}
private static URL[] getUrls(String branchName) {
def loaderDir = new File(loaderRoot, branchName)
List<File> files = []
loaderDir.eachFileRecurse{
if (it.name.endsWith('.jar')) {
files << it
}
}
List urls = files.sort{ it.name }.reverse().collect{it.toURI().toURL()}
return urls
}
}
要手动注册扩展方法模块,您可以使用类似于 GrapeIvy 中使用的代码。因为葡萄有同样的问题,它们使 jar 在错误的加载器中可见,但仍想启用扩展方法。有问题的代码是这样的:
JarFile jar = new JarFile(file)
def entry = jar.getEntry(ExtensionModuleScanner.MODULE_META_INF_FILE)
if (entry) {
Properties props = new Properties()
props.load(jar.getInputStream(entry))
Map<CachedClass, List<MetaMethod>> metaMethods = new HashMap<CachedClass, List<MetaMethod>>()
mcRegistry.registerExtensionModuleFromProperties(props, loader, metaMethods)
// add old methods to the map
metaMethods.each { CachedClass c, List<MetaMethod> methods ->
// GROOVY-5543: if a module was loaded using grab, there are chances that subclasses
// have their own ClassInfo, and we must change them as well!
Set<CachedClass> classesToBeUpdated = [c]
ClassInfo.onAllClassInfo { ClassInfo info ->
if (c.theClass.isAssignableFrom(info.cachedClass.theClass)) {
classesToBeUpdated << info.cachedClass
}
}
classesToBeUpdated*.addNewMopMethods(methods)
}
}
在此代码文件中是一个代表 jar 的文件。在你的情况下,你需要在这里有其他东西。基本上我们首先将描述符文件加载到属性中,调用 registerExtensionModuleFromProperties
以根据给定的 class 加载程序使用 MetaMethods 填充映射。这是解决问题的关键部分,这里正确的 class 加载器是可以加载扩展模块和 groovy 运行时中的所有 classes 的加载器!。在此之后,任何新元 class 都会知道扩展方法。仅当已经存在元 classes 时才需要后面的代码,您想了解这些新方法。
我在 java.util.ArrayList()
上创建了两个 groovy 扩展模块/方法。它在我的 IDE 中运行良好。我使用 gradle
构建 jar,并将其部署到远程 JVM
。当它到达远程 JVM
时,它失败了。
这里是扩展方法:
static Map sumSelectedAttributes(final List self, List attributes) {
Map resultsMap = [:]
attributes.each { attr ->
resultsMap[attr] = self.inject(0) { sum, obj ->
sum + obj[attr]
}
}
return resultsMap
这是调用它的代码:
outputMap[processName][iName] << kBeanList.sumSelectedAttributes([
"messageCount", "userCount", "outstandingRequests",
"cpuUsage", "memoryUsage", "threadCount", "cacheCount", "huserCount",
"manualHuserCount", "dataPointerCount", "tableHandleCount",
"jdbCacheRecordCount", "dbConnectionCount"])
这是错误:
No signature of method: java.util.ArrayList.sumSelectedAttributes() is applicable for argument types: (java.util.ArrayList) values: [[messageCount, incomingConnectionsCount, outgoingConnectionsCount, ...]]
同样,它在带有测试用例的 intellij 中运行良好。远程 JVM 上有什么不同会阻止它工作?以下是我想到的一些事情:
- 远程 JVM 使用 Groovy 2.3 而我在 2.4.5
- 我们在远程 JVM 上使用自定义 classloader 来加载 classes
除此之外,我找不到任何其他文档来说明我需要做什么才能使扩展在远程 JVM 上工作。
非常感谢任何帮助。
根据评论,似乎是自定义 classloader 的问题,这里是处理一些 classloader 操作的 class。
class CustomLoader {
static Map loaders = [:]
static File loaderRoot = new File("../branches")
static URLClassLoader getCustomLoader(String branchName) {
if (!loaders[branchName]) {
loaders[branchName] = new URLClassLoader(getUrls(branchName))
} else {
loaders[branchName]
}
}
static URLClassLoader updateClassLoader(String branchName) {
loaders[branchName] = null
loaders[branchName] = new URLClassLoader(getUrls(branchName))
}
private static URL[] getUrls(String branchName) {
def loaderDir = new File(loaderRoot, branchName)
List<File> files = []
loaderDir.eachFileRecurse{
if (it.name.endsWith('.jar')) {
files << it
}
}
List urls = files.sort{ it.name }.reverse().collect{it.toURI().toURL()}
return urls
}
}
要手动注册扩展方法模块,您可以使用类似于 GrapeIvy 中使用的代码。因为葡萄有同样的问题,它们使 jar 在错误的加载器中可见,但仍想启用扩展方法。有问题的代码是这样的:
JarFile jar = new JarFile(file)
def entry = jar.getEntry(ExtensionModuleScanner.MODULE_META_INF_FILE)
if (entry) {
Properties props = new Properties()
props.load(jar.getInputStream(entry))
Map<CachedClass, List<MetaMethod>> metaMethods = new HashMap<CachedClass, List<MetaMethod>>()
mcRegistry.registerExtensionModuleFromProperties(props, loader, metaMethods)
// add old methods to the map
metaMethods.each { CachedClass c, List<MetaMethod> methods ->
// GROOVY-5543: if a module was loaded using grab, there are chances that subclasses
// have their own ClassInfo, and we must change them as well!
Set<CachedClass> classesToBeUpdated = [c]
ClassInfo.onAllClassInfo { ClassInfo info ->
if (c.theClass.isAssignableFrom(info.cachedClass.theClass)) {
classesToBeUpdated << info.cachedClass
}
}
classesToBeUpdated*.addNewMopMethods(methods)
}
}
在此代码文件中是一个代表 jar 的文件。在你的情况下,你需要在这里有其他东西。基本上我们首先将描述符文件加载到属性中,调用 registerExtensionModuleFromProperties
以根据给定的 class 加载程序使用 MetaMethods 填充映射。这是解决问题的关键部分,这里正确的 class 加载器是可以加载扩展模块和 groovy 运行时中的所有 classes 的加载器!。在此之后,任何新元 class 都会知道扩展方法。仅当已经存在元 classes 时才需要后面的代码,您想了解这些新方法。