Archive

Posts Tagged ‘MOP’

A little bit of housework – MOPping up

December 31, 2016 Leave a comment

in doing some basics you keep relearning some stuff so here’s a quick recap on Groovy MOP.

Each groovy class has a metaClass, who’s default class is groovy.lang.MetaClassImpl.  If you set a property on the metaClass groovy changes this to be ExpandoMetaClass.  Normally calls to methods on your class are normally redirected to the metaClass to call the methods on your class using the invokeMethod call on the metaClass

Using the metaClass of your class you can lookup the methods on your class  like this

class MyIntercepted {

    def myMethods () {
        println "methods fom meta class"
        int total = 0
        def list = this.getMetaClass().getMethods().each {total++; println "$it.name"}
        println "total methods found : $total \n"
        list
    }

    def myMetaMethods () {
        println "list of meta methods from meta class"
        int total = 0
        def list = this.getMetaClass().getMetaMethods().each {total++; println "$it.name"}
        println "total of metaMethods found : $total \n"
        list
    }

    //has higher priority than invokeMethod
    /*def methodMissing (String name, args) {
        println "missing method: method $name ($args) was called by methodMissing"
    }*/

    //will be called if no method can be matched
    def invokeMethod (String name, args) {
        println "metaclass is of type " + this.getMetaClass().getClass()
        println "method $name ($args) was called intercepted by invokeMethod"
    }
}

// and use it 
MyIntercepted mi = new MyIntercepted()

mi.myMethods()       //class method exists
mi.myMetaMethods()   //class method exists
mi.smoke()           //class method doesn't exist 

 

calling myMethods() lists out the basic class methods found via the metaClass including those of GroovyObject, and my two class methods.

calling the myMetaMethods() will list out the broader list of metaMethods that Groovy embellishes each class with with via the GDK, which includes the “println” method.

if you implement def invokeMethod (String name, args), as shown (and you have to put the String type to name to get right dynamic dispatch), then when you call the nonexistent smoke(), method then Groovy calls the invokeMethod call where you can see the trace wriiten to your console.

if you uncomment the def methodMissing() this has higher priority and so this methodMissing() is called when the mi.smoke() method is called.

if you change your class to implement the marker interface GroovyInterceptable like this, then groovy intercepts all calls on the class and directs them to the invokeMethod call, including metaMethods such as println. Because of this you can’t use println as this is intercepted as wll and go into stackoverflow loop. you have to use the System.out.println to get trace output.

class MyIntercepted implements GroovyInterceptable {

    def myMethods () {
        println "methods from meta class"
        int total = 0
        def list = this.getMetaClass().getMethods().each {total++; println "$it.name"}
        println "total methods found : $total \n"
        list
    }

    def myMetaMethods () {
        println "list of meta methods fom meta class"
        int total = 0
        def list = this.getMetaClass().getMetaMethods().each {total++; println "$it.name"}
        println "total of metaMethods found : $total \n"
        list
    }

    //has higher priority than invokeMethod
    def methodMissing (String name, args) {
        println "missing method: method $name ($args) was called by methodMissing"
    }

    //will be called if no method can be matched
    def invokeMethod (String name, args) {
        System.out.println "method $name ($args) was called intercepted by invokeMethod"
    }
}

So how do call method if you have intercepted all the calls? You lookup the method and if it exists you can use the method.invoke(this, args) on it

    //will be called if no method can be matched
    def invokeMethod (String name, args) {
        //System.out.println "metaclass is of type " + this.getMetaClass().getClass()
        System.out.println "method $name ($args) was called intercepted by invokeMethod"
        MyIntercepted.metaClass.invokeMethod(this, 'println', "invoked println from metaClass.invokeMethod ")
        MetaMethod method = MyIntercepted.metaClass.getMetaMethod("hello")
        if (method) {
            System.out.println "looked up hello metamethod and tried to call it "
            method.invoke(this, "\tmethod.invoke: invoked hello metamethod")
            method.invokeMethod('println', "\tmethod.invokeMethod: invoked println directly from a meta method")
        }
    }

however you have to be careful like with println as this will again go into stack overflow

if you override the invokeMethod on the metaClass like this

MyIntercepted mi = new MyIntercepted()
mi.metaClass.invokeMethod = {String name, args -> System.out.println "used overriden metaClass invokeMethod"}

it has lower priority than a class method of same the name implemented in the class. whereas if you have a methodMissing on a class and one on the metaClass, the resolution path uses the metaClass implementation first!

Categories: Groovy Tags: ,
%d bloggers like this: