I want to create a wrapper that traps a particular exception and retries for all methods in a large (100+ methods) interface. I have the retry code working no worries, but I can't figure out how to hook up an implementation of the interface without cut'n'paste into all the methods.
I tried to use a missing method handler but that meant that I couldn't have it implement the interface. Abstract is obviously out as I won't be able to instantiate it.
I'm hoping for a better solution than creating the class as a template on the fly but I'm willing to do that.
Have you tried overriding invokeMethod for the interface?
YourInterface.metaClass.invokeMethod = {String name, args ->
def result
println "Calling method $name"
try{
result = metaClass.getMetaMethod(name, args).invoke(delegate, args)
}catch(YourException | AnyOtherException | Exception e){
println "Handling exception for method $name"
result = //Call retry code here
}
println "Called method $name"
result
}
Overriding invokeMethod works as as interceptor for all the method calls in the interface. Handle the exception for each method and return the success result.
I tried to use #dmahapatro's example but I kept getting IllegalArgumentException. I eventually realised that it only happened for mixin methods (the method shows the signature of the mixin). Instead of invoke() I needed to use doMethodInvoke() to get the appropriate type coersion.
errorProneInstance.metaClass.invokeMethod = { String name, args ->
def result
def method = delegate.metaClass.getMetaMethod(name, args)
while(true) {
try {
result = method.doMethodInvoke(delegate, args)
break
} catch (AnnoyingIntermittentButRetryableException e) {
print "ignoring exception"
}
}
result
}
Related
I've got a class that is wrapping objects with a proxy using Groovy's metaClass "getProperty" and "invokeMethod" methods. The proxy/wrapper lets me do lazy loading on the objects, only grab the data once the getter for a property has been called.
It works well when I actually use the getters, but I ran into a problem when I started trying to use Jackson to serialize the object (a secondary use case). I thought that Jackson would spin through all of the getter methods, which would invoke each of the getters and load all the data. But unlike when I call getters myself, when Jackson uses reflection to call the methods, my interceptor methods are not invoked, so all my data is still null.
I trace the call to my object in the Jackson library to this bit of code in the BeanPropertyWriter. The _accessorMethod.invoke(bean) call invokes the 'getter' method, but not the code in my proxy. I was able to recreate it in a unit test too outside of Jackson too.
Is there something I should implement in my Groovy proxy class that would capture those requests via the Method objects in reflection?
This is what my proxying code looks like:
static <T> T lazyProxyFor(T object) {
Class clazz = object.class
object.metaClass.getProperty = { String name ->
println "getting property $name"
def currentVal = clazz.metaClass.getMetaProperty(name).getProperty(delegate)
if (!currentVal) {
setProperty('testing') //normally this goes and gets data from my service
}
return currentVal
}
object.metaClass.invokeMethod = { String name, Object args ->
println "calling $name"
if (name ==~ /get[A-Z][a-zA-Z0-9_]*/ && args?.size() == 0) {
object[name.drop(3).uncapitalize()] // call the getProperty method
} else {
clazz.metaClass.getMetaMethod(name, args)?.invoke(delegate, args) //like the underlying object process the
}
}
return object
}
and this is the code I was testing with:
Address address = lazyProxyFor(Address)
address.class.declaredMethods.each{ method ->
if (method.name.startsWith('get')) {
method.invoke(address) //<-- if I run with this version I get no output
//address."$method.name"() //<-- if I run with this version I get two messages, one for each
//getter method and then the underlying property, presummably because
//groovy is more involved here, instead of reflection
}
}
A closure with a delegate that is not a groovy-object (e.g. coming from a normal java-library), will never call the 'methodMissing' object added to that delegate using it's metaclass, if the call is made 'implicit' (i.e. not calling it explicitly on 'delegate' within the closure.)
The code below does an explicit and an implicit call to a non-existing method; it does so on a Groovy-class instance, a GString and a non-groovy object. The only one that fails is the implicit call to a non-groovy-object (i.c. ArrayList).
(You can see and run the same code online:
https://groovyconsole.appspot.com/edit/5200829376102400)
Not sure if this is a bug or a limitation - references to methodMissing defined through metaClass are pretty scarce. Any insightful comments would be welcome.
class ClosureDelegate {
def testMissingMethod(def someObject) {
someObject.metaClass.methodMissing = { String name, args ->
println name
}
def closure = {
delegate.anything()
anything() // this one fails on non-groovyclasses
}
closure.delegate = someObject
closure.resolveStrategy = Closure.DELEGATE_ONLY
closure()
}
}
class TestObject {}
println "testing with TestObject"
new ClosureDelegate().testMissingMethod(new TestObject())
println "testing with GString"
new ClosureDelegate().testMissingMethod("${new Date()}")
println "testing with ArrayList"
new ClosureDelegate().testMissingMethod(new ArrayList())
testing with TestObject
anything
anything
testing with GString
anything
anything
testing with ArrayList
anything
Caught: groovy.lang.MissingMethodException: No signature of method: ClosureDelegate$_testMissingMethod_closure2.anything() is applicable for argument types: () values: []
Possible solutions: toString(), toString(), any(), any()
According to the ClosureMetaClass implementation, this behavior is expected. Take a look at the following part that starts at line 275 in that file:
switch (resolveStrategy) {
case Closure.TO_SELF:
break;
case Closure.DELEGATE_ONLY:
method = getDelegateMethod(closure, delegate, methodName, argClasses);
callObject = delegate;
if (method == null) {
invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);
}
break;
Source: src/main/java/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java#L275-L284
The invokeOnDelegate boolean flag verifies if the delegate object extends GroovyObject (the default parent class for all Groovy classes.) When you execute your code with Groovy classes, this flag is set to true and the anything method from the delegate object gets invoked. In the case of non-Groovy classes, MethodMissingException is thrown.
You can debug this behavior by setting the breakpoint in AbstractCallSite.callCurrent(GroovyObject receiver) line 160. You will see that in all cases the method anything is not found in the closure, but in the first two cases invokeOnDelegate is evaluated to true, and the invokeMethod on delegate object is executed. It does not happen in the third case, because ArrayList is not an instance of GroovyObject.
Inside a spock test we want to create a resource and make sure its disposed correctly regardless of the outcome of the test result.
We tried the approach below. But spock is not executing tests when the test code is wrapped inside a closure.
import spock.lang.Specification
class ExampleSpec extends Specification {
def wrapperFunction(Closure cl) {
try {
cl()
} finally {
// do custom stuff
}
}
def "test wrapped in closure"() {
wrapperFunction {
expect:
1 == 1
println "will not execute!"
}
}
}
What is the best approach on creating and disposing a resource inside a spock test.
setup() and cleanup() are not viable solutions since creating and disposing should be possible at arbitrary points inside the feature method.
You can use setup and cleanup block inside of the test case (feature method) like this:
class ReleaseResourcesSpec extends Specification {
void 'Resources are released'() {
setup:
def stream = new FileInputStream('/etc/hosts')
when:
throw new IllegalStateException('test')
then:
true
cleanup:
stream.close()
println 'stream was closed'
}
}
Code from the cleanup block is always executed although the test fails or if there is any exception. See the result of the above example:
So it is similar to setup() and cleanup() methods but in this case you can have different setup and clean up code for each feature method.
First look at the following Groovy code:
class Car {
def check() { System.out.println "check called..." }
def start() { System.out.println "start called..." }
}
Car.metaClass.invokeMethod = { String name, args ->
System.out.print("Call to $name intercepted... ")
if (name != 'check') {
System.out.print("running filter... ")
Car.metaClass.getMetaMethod('check').invoke(delegate, null)
}
def validMethod = Car.metaClass.getMetaMethod(name, args)
if (validMethod != null) {
validMethod.invoke(delegate, args)
} else {
Car.metaClass.invokeMissingMethod(delegate, name, args)
}
}
car = new Car()
car.start()
The output is:
Call to start intercepted... running filter... check called...
start called...
According to the Groovy method dispatching mechanism I think the start method in the Car should be called directly instead of being intercepted by the invokeMethod in the Car's metaClass. Why is the start method intercepted by the invokeMethod? How is the invokeMethod invoked when a method is called on an object?
If you can give me some detailed explanations about Groovy method dispatching mechanism(MOP) I will appreciate that.
In short you are not using the standard meta class, so you don't get the standard Groovy MOP.
Car.metaClass.invokeMethod = { will let Car have an ExpandoMetaClass as meta class. This meta class uses the invokeMethod you give in as open block (like you do) to intercept calls. This is very different from defining an invokeMethod in the class itself.
I'm trying to intercept all calls to properties on a Groovy class. Since this did not work as expected, I created the following example:
class TestClass {
def getProperty(String key) {
println "getting property: " + key
}
def invokeMethod(String method, args) {
println "invoking method: " + method
}
def getFoo() {
return 1
}
}
tc.foo // 1
tc.getFoo() // 2
1) does the right thing, that is getProperty is called. However, 2) works (i.e. 1 is returned) but neither getProperty nor invokeMethod is called.
Is there a way to intercept the getfoo() call as well?
Stefan
I wrote an article a couple of months ago. You can read it here.
Try this code :
TestClass.metaClass.invokeMethod = {
def metaMethod = delegate.metaClass.getMetaMethod(method,args)
println "executing $method with args $args on $delegate"
return metaMethod.invoke(delegate,args)
}
I had to modify the code in a previous answer a bit to get what I think you want:
TestClass.metaClass.invokeMethod = {method, args ->
def metaMethod = TestClass.metaClass.getMetaMethod(method,args)
println "executing $method with args $args on $delegate"
metaMethod.invoke(delegate,args) // could result in NPE
}
Then executing
tc.foo
tc.getFoo()
Results in:
getting property: foo // println output
null // getProperty return is null
executing getFoo with args [] on TestClass#655538e5 // invokeMethod output
1 // metaMethod invocation
The problem is that there are two different kinds of paths how a request is handled is used here. For asking properties the getProperty method is called before we go into the meta class - if you overwrite getProperty you have to do the meta class call yourself actually. In case of invokeMethod it is normally asked after the meta class has been asked. Since the meta class will respond to your asking for getFoo(), invokeMethod will not be asked at all. If you let the class implement GroovyInterceptable then invokeMethod is asked first, the same way as getProperty. That also explains why the ways using the meta class instead do work.