So in the last lesson we covered what you can do with metaClasses and Groovy to print some info about the classLoader.
This is about how to make that a little easier on you. I was originally going to work this as using ExpandoMetaClass, but there were some things it did, that I didn’t need and didn’t want. EMC is great for when you want to tie a closure to a specific type like File, but becomes cumbersome if you want to do this for multiple Object types.
The rules I came up with for this use:
1) It must utilize a closure
2) It must utilize dynamic method sets
3) It must support static and instance setting of a closure on the metaClass
So let’s start with number 1… not too bad. It’s a copy and paste from the previous article with a few formatting preferences made on my part.
def pl = {Object c -> def loader = c.getClass().classLoader println "#" * 30 print "#" print "Class Loader Structure for class ${delegate.getClass()}".center(28) println "#" while (loader != null) { println loader loader = loader.parent } println "#" * 30 }
Now what I wanted to do was add this closure to one or more Object metaClasses… so this could be added to any String implementation, Object implementation, or whatever.
def type = [Object, String]
Now it’s a list of Ojbects that I want to add a metaClass to. Not the most efficient way, but you now control what Objects and objects get what added to them.
So now let’s explore dynamically adding this metaClass method to our Class.
We know from the previous example that we have one test case:
'bob'.printLoaderStructure(this)
We definitely want to keep that, so let’s look at a simple closure to add that support.
def pushMeta = {lst, c, methodName -> lst.each { it.metaClass."${methodName}" = c } }
Let’s break this down a little bit… so we have a Closure called pushMeta, in this case it takes a List, a Closure, and a String representation of a methodName.
We also know we’re going to iterate through the list to get our object and set the metaClass… so what I’ve done is tell us that it.metaClass is going to get a new function that it can run. We don’t know what it’s called yet, so we’re utilizing a GString (the braces are optional, I usually add them for clarity, but not always). that new function is going to be the Closure we’ve passed in. Lost yet?
We can now put any closure with our object.metaClass.anymethodnamewewant… let’s see that in action.
pushMeta(type, pl, "printLoaderStructure")
Wow, what was that? So we’re calling our pushMeta Closure, we’re passing our object type list, the original printloader Closure and a String called printLoaderStructure
Our pushMeta is going to dynamically create the following:
Object.metaClass.printLoaderStructure = {Object c -> def loader = c.getClass().classLoader println "#" * 30 print "#" print "Class Loader Structure for class ${delegate.getClass()}".center(28) println "#" while (loader != null) { println loader loader = loader.parent } println "#" * 30 }
So now these examples will work:
new Object().printLoaderStructure(this) 'bob'.printLoaderStructure(this)
That’s fine, you say, but what about static. Well, for simplicity, I’m going to add a specific closure for static.
def pushMetaStatic = {lst, c, methodName -> lst.each { it.metaClass.'static'."${methodName}" = c } }
I could work a lot on this and make the static identifier part of a deeper longing for my mother, crap, got psychology mixed with code again. I could programmatically add static, but for now, this works beautifully.
As I stated in the last post static is a keyword so we can’t just do:
Object.metaClass.static the compiler and the rest of our code will not wait until the next morning to hate you.
pushMetaStatic(type, pl, "printLoaderStructure")
Will now do the same thing as pushMeta but add ‘static’ after the metaClass
Object.metaClass.'static'.printLoaderStructure = {Object c -> def loader = c.getClass().classLoader println "#" * 30 print "#" print "Class Loader Structure for class ${delegate.getClass()}".center(28) println "#" while (loader != null) { println loader loader = loader.parent } println "#" * 30 }
Now the following tests work:
Object.printLoaderStructure(this) String.printLoaderStructure(this)
I’ll post a code download shortly.