问题
Background
Groovy have the feature of adding methods to the existing classes, and I've found some interesting ones.
Then I discovered that I need to customize my Grails bootstrap to load them, so I add:
def init = { servletContext -> addExtensionModules() }
def addExtensionModules() {
Map<CachedClass, List<MetaMethod>> map = [:]
ClassLoader classLoader = Thread.currentThread().contextClassLoader
try {
Enumeration<URL> resources = classLoader.getResources(MetaClassRegistryImpl.MODULE_META_INF_FILE)
for (URL url in resources) {
if (url.path.contains('groovy-all')) {
// already registered
continue
}
Properties properties = new Properties()
InputStream inStream
try {
inStream = url.openStream()
properties.load(inStream)
GroovySystem.metaClassRegistry.registerExtensionModuleFromProperties(properties,
classLoader, map)
}
catch (IOException e) {
throw new GroovyRuntimeException("Unable to load module META-INF descriptor", e)
} finally {
inStream?.close()
}
}
} catch (IOException ignored) {}
map.each { CachedClass cls, List<MetaMethod> methods ->
cls.setNewMopMethods(methods)
}
}
And I add in my BuildConfig.groovy
compile ('ca.redtoad:groovy-crypto-extensions:0.2') {
excludes 'groovy-all'
}
The Question
The problem is that now I cannot use the toBoolean()
method of Groovy String:
groovy.lang.MissingMethodException: No signature of method: java.lang.String.toBoolean() is applicable for argument types: () values: [] Possible solutions: asBoolean(), asBoolean(), toFloat(), toDouble()
Since groovy is already registered, why the method is missing? I'm using Grails 2.2.4.
EDIT
Tested in a groovy 2.0.8 console, and the code works, so probably is something related to Grails.
@Grab('ca.redtoad:groovy-crypto-extensions:0.2')
@GrabExclude('org.codehaus.groovy:groovy-all')
addExtensionModules() //same method of BootStrap, ommited to make shorter.
def key = "password".toKey()
def ciphertext = "some plaintext".bytes.encrypt(key: key)
def x = new String(ciphertext.decrypt(key: key)).toBoolean()
println "S".toBoolean()
回答1:
Replace
map.each { CachedClass cls, List<MetaMethod> methods ->
cls.setNewMopMethods(methods)
}
with
map.each { CachedClass cls, List<MetaMethod> methods ->
//Add new MOP methods instead of set them as new
cls.addNewMopMethods(methods)
}
When new meta method is set in the CachedClass
the existing extensions/meta methods are overriden by the only provided extension from the extension module. In this case, groovy-crypto-extension
uses the below extension methods on String class
class java.lang.String=
[public static javax.crypto.spec.SecretKeySpec ca.redtoad.groovy.extensions.crypto.CryptoExtensionMethods.toKey(java.lang.String),
public static javax.crypto.spec.SecretKeySpec ca.redtoad.groovy.extensions.crypto.CryptoExtensionMethods.toKey(java.lang.String,java.util.Map)
]
If those methods are set to the CachedClass, the existing methods are wiped out. So it has to be replace with adding them to the CachedClass. Hence, making toBoolean
available on String class.
Challenge accepted and executed. You owe me a treat. (Gift Card is acceptable too). ;)
来源:https://stackoverflow.com/questions/19564902/applying-groovy-extensions-in-grails-produces-missingmethodexception-for-string