intercepting LOCAL property access in groovy - groovy

I am running into a problem while trying to use property access in Groovy. Take the following class:
class Foo {
Map m = [:]
String bar
void getProperty(String name) {
m.get name
}
def setProperty(String name, value) {
m.set name, value
}
String getBarString() {
return bar // local access, does not go through getProperty()
}
}
It overrides the getter and setter to simply place the values into a Map rather than into the object's normal property space. In the abstract this is a bit silly, but imagine that instead of placing the data into a map we were persisting it to a DB or something else useful.
Unfortunately, the following code now won't work:
foo = new Foo()
foo.bar = "blerg" // using foo.bar invokes the setProperty interceptor
assert foo.bar == "blerg" // this will work fine as foo.bar here uses the getProperty interceptor
assert foo.getBarString() == "blerg" // explosion and fire! getBarString accesses bar locally without going through the getProperty interceptor so null will actually be returned.
Certainly there are workarounds for this, setProperty could set both the MetaProperty and the Map value, etc. However, all of the strategies I've thought of require a lot of extra caution from the programmer to make sure they are accessing class properties in the exact way that they mean to.
Furthermore, some of the built in awesome stuff in Groovy (like #Delegate for example) uses direct MetaProperty access rather than going through getProperty so the following would never work:
class Meep {
String getMyMeep() {
return "MEEP!!!"
}
}
class Foo {
Map m = [:]
String bar
#Delegate Meep meep
void getProperty(String name) {
m.get name
}
def setProperty(String name, value) {
m.set name, value
}
String getBarString() {
return bar
}
}
foo = new Foo()
foo.meep = new Meep() // uses setProperty and so does not place the Meep in the Map m
foo.getMyMeep()
A null pointer exception is thrown on the last line as #Delegate uses MetaProperty direct access (effectively this.meep.getMyMeep() rather than the getProperty interceptor. Unfortunately 'meep' is null, though getProperty('meep') would not be.
In short what I'm looking for is a strategy to solve the following criteria:
intercept property read/write to enable automatic alternative data storage
transparent or near-transparent interface for other developers (I don't want to make other folks' lives significantly harder)
allow for local access of variables using the MetaProperty/this/etc. access methods
Thanks in advance!

You could use
foo.#meep = new Meep()
in order to directly access properties bypassing setProperty method.
That doesn't completely solves your problem though as the foo.meep still triggers set/getProperty.
Another way you could go about is by using getter and setter of the meet directly, i.e.
foo.setMeep(new Meep())
So, one unified way would be to define all of the variables as private and use get/set*PropertyName*

By using an AST Transformation I can do the following:
walk a class's structure and rename all local fields to something like x -> x.
add a getter/setter for each renamed field like this
def get_x_() {
x
}
...in order to access x as a field rather than as a Groovy property
- now apply the transformation on the following class
class Foo {
def x
def y
Map m = [:]
#Delegate Date date // for testing if non-local fields work
def getProperty(String name) {
if (this.respondsTo("get__${name}__")) // if this is one of our custom fields
return "get__${name}__"()
"get${Verifier.capitalize(name)}"() // pass to specific getter method
}
void setProperty {
if (this.respondsTo("set__${name}__")) {
"set__${name}__"(value)
m[name] = value
if (name == "x") y = x + 1
return
}
"set${Verifier.capitalize(name)}"(value)
}
}
now run a testing method like this:
public void testAST() {
def file = new File('./src/groovy/TestExample.groovy')
GroovyClassLoader invoker = new GroovyClassLoader()
def clazz = invoker.parseClass(file)
def out = clazz.newInstance()
out.x = 10
assert out.y == 11
out.y = 5
assert out.y == 5
out.x = 2
assert out.m.containsKey('x')
assert out.m.x == 2
assert out.m.y == 3
out.date = new Date()
assert out.time && out.time > 0
}
And everything should work out including m getting updated, date delegate method time getting accessed properly, etc.
-Glenn

Related

Why is Groovy metaClass .static changes using MOP not behaving as expected

I have set up a simple dummy class as follows, and used a static initialiser to update the metaClass:
class DynamicExtendableClass {
static String declaredStaticString = "declared static string"
static String getDeclaredMethodStaticString () {
"static method returning string"
}
static {
println "static initialiser - adding dynamic properties and methods to metaClass"
DynamicExtendableClass.metaClass.addedProperty = "added property to class metaClass"
DynamicExtendableClass.metaClass.getAddedMethod = { -> "added closure as method" }
DynamicExtendableClass.metaClass.static.getStaticAddedMethod = { -> "added closure as static method" }
}
}
I have a simple test case like this:
#Test
void testExtendedMetaClassStuff () {
DynamicExtendableClass testInstance = new DynamicExtendableClass()
assertEquals ("added property to class metaClass", testInstance.addedProperty)
assertEquals ("added closure as static method", testInstance.getStaticAddedMethod()) //calls getStaticAddedMethod - groovy trick
assertEquals ("added closure as method", testInstance.addedMethod) //works. calls getAddedMethod - groovy trick for getXxx as property
assertEquals ("added closure as static method", DynamicExtendableClass.staticAddedMethod ) //works class static class Closure
}
Which works only once you create a first instance of the class which forces a switch to ExpandoMetaClass for you.
If you don't do this first the default HandleMetaClassImpl doesn't work for this.
However to get this to work for static you have to create closure like getXxxx = {-> ...}, which if you call 'DynamicExtendableClass.staticAddedMethod' will sneakily invoke the closure for you.
However, there's not really a means to add a property capability here for '.static' as there is on the standard metaClass itself. All you can do is set a closure onto .static. Why is this?
The other problem is having to create an instance of the class first to force the switch to ExpandoMetaClass, is there not a simple way to force the metaClass change when declaring the class in the first class, before creating any instances ?
I want to add some static properties (later some methods maybe ) dynamically to a class, but all you can add is static closures, which is a little limiting on the scenario I had in mind.
PostScript
I managed to force a change of metaClass on class without having to create an instance, but it's a bit hard work:
#Test
void testMetaClassStatic () {
println DynamicExtendableClass.metaClass
MetaClassRegistry registry = GroovySystem.getMetaClassRegistry()
MetaClass origMC = registry.getMetaClass(DynamicExtendableClass)
assert origMC.getClass() == HandleMetaClass //default implementation
ExpandoMetaClass emc = new ExpandoMetaClass (DynamicExtendableClass, true, true)
emc.static.getStaticAddedMethod = {-> "static hello from my emc"}
emc.initialize()
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, emc)
assert DynamicExtendableClass.metaClass.getClass() == ExpandoMetaClass
assert DynamicExtendableClass.staticAddedMethod == "static hello from my emc"
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, origMC)
}
But doing this breaks my previously working tests (not sure why) with:
Could not initialize class extensible.DynamicExtendableClass
java.lang.NoClassDefFoundError: Could not initialize class extensible.DynamicExtendableClass
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:73)
at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:108)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:59)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:263)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:268)
at extensible.DynamicExtendableClassTest.testExtendedMetaClassStuff(DynamicExtendableClassTest.groovy:22)
at ...
Another postscript
I did a little exploration with a debugger. 1st the metaClass.static returns a class of type ExpandoMetaClass.ExpandoMetaProperty which of itself isn't terribly useful. You can do a direct .#this$0 field access however which just points the same metaClass instance as the target class you start with.
Therefore ignoring this you can do a direct field grab on <yourClass>.metaClass.#expandoProperties (I tried to get this via reflection using:
PropertyValue expandoProperties = clazz.metaClass.getMetaPropertyValues().find{it.name == 'expandoProperties'}
List<MetaBeanProperty> MBprops2= properties.getValue()
Map m2 = MBprops.findAll{Modifier.isPublic(it.modifiers)}.collectEntries{[(it.name), it.getProperty(clazz)] }
but it doesn't get the same content as the direct field access does.
The direct field access returns a Map where the key is the string value of any added closures or properties added dynamically to the metaClass, and the value is a MetaBeanProperty reference.
On that MetaBeanProperty you can invoke the getProperty (object) using with the class metaClass or per instance metaClass - and it returns the value of that property (whether it's just a closure or a real property) for you. You can also test whether its static or not:
Map m4 = thisMc.#expandoProperties
MetaBeanProperty asm = m4['addedStaticMethod']
def val2 = asm.getProperty(clazz)
boolean isstatic = Modifier.isStatic(asm.modifiers)
Kind of brutal but it sort of works if you want to dynamically query the dynamic editions to the metaclass.
The problem of forcing the switch from default metaClass to the ExpandoMetaClass remains a problem. The best way seems to create a throw away class instance as this does the one time switch for you.
I tried to force this myself using the metaClass registry which you can do, but then the future create new instance for your class seems to stop working ie. doing somethings like this and putting the original back afterwords seems to break any future new <MyClass>() calls.
MetaClassRegistry registry = GroovySystem.getMetaClassRegistry()
MetaClass origMC = registry.getMetaClass(DynamicExtendableClass)
assert origMC.getClass() == MetaClassImpl //default implementation
def constructors = MetaClassImpl.getConstructors()
ExpandoMetaClass emc = new ExpandoMetaClass (DynamicExtendableClass, true, true)
emc.static.getStaticAddedMethod = {-> "static hello from my emc"}
emc.constructor = { new DynamicExtendableClass() }
emc.initialize()
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, emc)
assert DynamicExtendableClass.metaClass.getClass() == ExpandoMetaClass
assert DynamicExtendableClass.staticAddedMethod == "static hello from my emc"
registry.removeMetaClass(DynamicExtendableClass)
registry.setMetaClass(DynamicExtendableClass, origMC)

Groovy - interceptor of ProxyMetaClass does not affect inner methods calls

The code snippet is from the book < Groovy in action 2nd >, with minor modifications.
1 this code works as expected
package test
class InspectMe {
int outer(){
return inner()
}
int inner(){
return 1
}
}
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
inspectMe.metaClass = proxyMetaClass
inspectMe.outer()
println(tracer.writer.toString())
output:
before test.InspectMe.outer()
before test.InspectMe.inner()
after test.InspectMe.inner()
after test.InspectMe.outer()
2 but this code's output is different
package test
class InspectMe {
int outer(){
return inner()
}
int inner(){
return 1
}
}
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
proxyMetaClass.use(inspectMe){
inspectMe.outer()
}
println(tracer.writer.toString())
output:
before test.InspectMe.outer()
after test.InspectMe.outer()
It seems TracingInterceptor dosen't intercept inner methods in the second code.
Maybe it's normal behavior, But it seems to me like a bug.
Can somebody please explain this?
I don't know if this is a bug or not, but I can explain why this different behavior happens. Let's start with analyzing what InspectMe.outer() method implementation looks like at the bytecode level (we decompile .class file):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.BytecodeInterface8;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
public class InspectMe implements GroovyObject {
public InspectMe() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public int outer() {
CallSite[] var1 = $getCallSiteArray();
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.inner() : DefaultTypeTransformation.intUnbox(var1[0].callCurrent(this));
}
public int inner() {
CallSite[] var1 = $getCallSiteArray();
return 1;
}
}
As you can see, the outer() method tests the following predicate
!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()
and if it evaluates to true, it invokes directly this.inner() method avoiding Groovy's MOP (meta-object protocol) layer (no metaclass involved in this case). Otherwise, it invokes var1[0].callCurrent(this) which means that inner() method gets invoked through Groovy's MOP with metaclass and interceptor involved in its execution.
The two examples you have shown in the question present a different way of setting metaclass field. In the first case:
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
inspectMe.metaClass = proxyMetaClass // <-- setting metaClass with DefaultGroovyMethods
inspectMe.outer()
println(tracer.writer.toString())
we are invoking inspectMe.setMetaClass(proxyMetaClass) method using Groovy's MOP layer. This method gets added to InspectMe class by DefaultGroovyMethods.setMetaClass(GroovyObject self, MetaClass metaClass).
Now, if we take a quick look at how this setMetaClass method is implemented we will find something interesting:
/**
* Set the metaclass for a GroovyObject.
* #param self the object whose metaclass we want to set
* #param metaClass the new metaclass value
* #since 2.0.0
*/
public static void setMetaClass(GroovyObject self, MetaClass metaClass) {
// this method was introduced as to prevent from a stack overflow, described in GROOVY-5285
if (metaClass instanceof HandleMetaClass)
metaClass = ((HandleMetaClass)metaClass).getAdaptee();
self.setMetaClass(metaClass);
disablePrimitiveOptimization(self);
}
private static void disablePrimitiveOptimization(Object self) {
Field sdyn;
Class c = self.getClass();
try {
sdyn = c.getDeclaredField(Verifier.STATIC_METACLASS_BOOL);
sdyn.setBoolean(null, true);
} catch (Throwable e) {
//DO NOTHING
}
}
It invokes at the end private method disablePrimitiveOptimization(self). This method is responsible for assigning true to __$stMC class field (the constant Verifier.STATIC_METACLASS_BOOL stores __$stMC value). What does it mean in our case? It means that the predicate in outer() method:
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.inner() : DefaultTypeTransformation.intUnbox(var1[0].callCurrent(this));
evaluates to false, because __$stMC is set to true. And in this case inner() method gets executed via MOP with metaClass and interceptor.
OK, but it explains the first case that works as expected. What happens in the second case?
def tracer = new TracingInterceptor(writer: new StringWriter())
def proxyMetaClass = ProxyMetaClass.getInstance(InspectMe)
proxyMetaClass.interceptor = tracer
InspectMe inspectMe = new InspectMe()
proxyMetaClass.use(inspectMe){
inspectMe.outer()
}
println(tracer.writer.toString())
Firstly, we need to check what does proxyMetaClass.use() look like:
/**
* Use the ProxyMetaClass for the given Closure.
* Cares for balanced setting/unsetting ProxyMetaClass.
*
* #param closure piece of code to be executed with ProxyMetaClass
*/
public Object use(GroovyObject object, Closure closure) {
// grab existing meta (usually adaptee but we may have nested use calls)
MetaClass origMetaClass = object.getMetaClass();
object.setMetaClass(this);
try {
return closure.call();
} finally {
object.setMetaClass(origMetaClass);
}
}
It's pretty simple - it replaces metaClass for the time of closure execution and it sets the old metaClass back when closure's execution completes. Sounds like something similar to the first case, right? Not necessarily. This is Java code and it invokes object.setMetaClass(this) method directly (the object variable is of type GroovyObject which contains setMetaClass method). It means that the field __$stMC is not set to true (the default value is false), so the predicate in outer() method has to evaluate:
BytecodeInterface8.disabledStandardMetaClass()
If we run the second example we will see that this method call returns false:
And that is why the whole expression
!__$stMC && !BytecodeInterface8.disabledStandardMetaClass()
evaluates to true and the branch that invokes this.inner() directly gets executed.
Conclusion
I don't know if it was intended or not, but as you can see dynamic setMetaClass method disables primitive optimizations and continues using MOP, while ProxyMetaClass.use() sets the metaClass keeping primitive optimizations enabled and caused a direct method call. I guess this example shows a corner case no one thought about when implementing ProxyMetaClass class.
UPDATE
It seems like the difference between these two methods exists because ProxyMetaClass.use() was implemented in 2005 for Groovy 1.x and it got updated for the last time in 2009. This __$stMC field was added in 2011 and the DefaultGroovyMethods.setMetaClass(GroovyObject object, Closure cl) was introduced in 2012 according to its javadoc that says this method is available since Groovy 2.0.

Setting default property value when using groovy #Builder

It seems that setting a default value for a class property, is not honored by #Builder.
#Test
void test() {
assert Foo.builder().build().getProp() != null // fail
}
#Builder
class Foo {
Map prop = [:]
}
I'll probably fix this by overriding the build method but how?
Not really sure about the implementation of builder() method of #Builder.
I believe you need to initialize the properties / members of the class, then do .build() to create the instance of the class.
Here is the example:
import groovy.transform.builder.Builder
#Builder
class Foo {
Map prop
}
def map = [a:1, b:2]
def f = Foo.builder().prop(map).build()
assert map == f.prop // or you can use f.getProp()
You can quickly try it online Demo
If you notice, the demo example shows how you can initialize multiple properties while building the object.

Groovy: Is there a better way of handling #Immutable objects than copyWith method

I am looking for a flexible way of "modifying" (copying with some values changed) immutable objects in groovy. There is a copyWith method but it allows you only to replace some properties of the object. It doesn't seem to be convenient enough.
Let's say we have a set of classes representing a domain design of some system:
#Immutable(copyWith = true)
class Delivery {
String id
Person recipient
List<Item> items
}
#Immutable(copyWith = true)
class Person {
String name
Address address
}
#Immutable(copyWith = true)
class Address {
String street
String postalCode
}
Let's assume I need to change street of delivery recipient. In case of regular mutable object it is just fine to perform:
delivery.recipient.address.street = newStreet
or (perhaps useful in some cases):
delivery.with {recipient.address.street = newStreet}
When it comes to do the same with immutable objects the best way according to my knowledge would be:
def recipient = delivery.recipient
def address = recipient.address
delivery.copyWith(recipient:
recipient.copyWith(address:
address.copyWith(street: newStreet)))
It is actually needed for Spock integration test code so readability and expressiveness matters. The version above cannot be used "on the fly" so in order to avoid creating tons of helper methods, I have implemented my own copyOn (since copyWith was taken) method for that which makes it possible to write:
def deliveryWithNewStreet = delivery.copyOn { it.recipient.address.street = newStreet }
I wonder however if there is an ultimate solution for that, present in groovy or provided by some external library. Thanks
For the sake of completeness I provide my implementation of copyOn method. It goes as follows:
class CopyingDelegate {
static <T> T copyOn(T source, Closure closure) {
def copyingProxy = new CopyingProxy(source)
closure.call(copyingProxy)
return (T) copyingProxy.result
}
}
class CopyingProxy {
private Object nextToCopy
private Object result
private Closure copyingClosure
private final Closure simplyCopy = { instance, property, value -> instance.copyWith(createMap(property, value)) }
private final def createMap = { property, value -> def map = [:]; map.put(property, value); map }
CopyingProxy(Object nextToCopy) {
this.nextToCopy = nextToCopy
copyingClosure = simplyCopy
}
def propertyMissing(String propertyName) {
def partialCopy = copyingClosure.curry(nextToCopy, propertyName)
copyingClosure = { object, property, value ->
partialCopy(object.copyWith(createMap(property, value)))
}
nextToCopy = nextToCopy.getProperties()[propertyName]
return this
}
void setProperty(String property, Object value) {
result = copyingClosure.call(nextToCopy, property, value)
reset()
}
private void reset() {
nextToCopy = result
copyingClosure = simplyCopy
}
}
It is then just a matter of adding the delegated method in Delivery class:
Delivery copyOn(Closure closure) {
CopyingDelegate.copyOn(this, closure)
}
High level explanation:
First of all it is required to notice that the code of: delivery.recipient.address.street = newStreet is interpreted as:
Accessing recipient property of delivery object
Accessing address of what was the result of the above
Assigning property street with the value of newStreet
Of course the class CopyingProxy does not have any of those properties, so propertyMissing method will be involved.
So as you can see it is a chain of propertyMissing method invocations terminated by running setProperty.
Base case
In order to implement the desired functionality we maintain two fields: nextToCopy (which is delivery at the beginning) and copyingClosure (which is initialised as a simple copy using copyWith method provided by #Immutable(copyWith = true) transformation).
At this point if we had a simple code like delivery.copyOn { it.id = '123' } then it would be evaluated as delivery.copyWith [id:'123'] according to simplyCopy and setProperty implementations.
Recursive step
Let's now see how would it work with one more level of copying: delivery.copyOn { it.recipient.name = 'newName' }.
First of all we will set initial values of nextToCopy and copyingClosure while creating CopyingProxy object same way as in the previous example.
Let's now analyse what would happen during first propertyMissing(String propertyName) call. So we would capture current nextToCopy (delivery object), copyingClosure (simple copying based on copyWith) and propertyName (recipient) in a curried function - partialCopy.
Then this copying will be incorporated in a closure
{ object, property, value -> partialCopy(object.copyWith(createMap(property, value))) }
which becomes our new copyingClosure. In the next step this copyingClojure is invoked in the way described in Base Case part.
Conclusion
We have then executed: delivery.recipient.copyWith [name:'newName']. And then the partialCopy applied to the result of that giving us delivery.copyWith[recipient:delivery.recipient.copyWith(name:'newName')]
So it's basically a tree of copyWith method invocations.
On top of that you can see some fiddling with result field and reset function. It was required to support more than one assignments in one closure:
delivery.copyOn {
it.recipient.address.street = newStreet
it.id = 'newId'
}

Groovy metaClass closures vs dynamic mixins

So I want to add methods to JDK classes like InputStream, File, etc. I'm trying to figure out what is the best way to do that, but it seems there are several options for doing it. One way is do this by adding methods into the metaClass property on the Class like so:
InputStream.metaClass.copy = { OutputStream out ->
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return delegate
}
Another way is using dynamic mixins like this:
class EnhancedInputStream {
static {
InputStream.metaClass.mixin( EnhancedInputStream )
}
public InputStream copy( OutputStream out ) {
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = mixinOwner.read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return mixinOwner
}
}
So the first question is do dynamic Mixins replace the use of using metaClass + Closure to create mixins? The examples of dynamic mixins don't really discuss scoping rules in any detail that I can find. Which leads me to the next point.
You can see in the first code sample using metaClass I used delegate to get access to the this pointer of the class I was adding methods to. What is the equivalent way to do that using dynamic Mixins? All examples I've found are stateless (pointless really). I found one example mentioning a special member mixinOwner that could be used in place of delegate. Is that true?
Second you'll see I used a static block in EnhancedInputStream to add the mixin dynamically to InputStream. When using metaClass what is the best way to add those? Another static block with import statement?
I suppose I really want just a compile time Mixin where I can define the #Mixin on the source of the mixin instead of destination because I didn't write the destination. Like
#MixinInto(File)
public class EnhancedFileMixin {
public void zip( File output ) {
// .....
}
}
But that doesn't appear to exist in Groovy land. So what's the best approach to reach this using metaClass or dynamic mixins?
I guess the nearest to #MixinInto would be the magic package convention. I couldn't mix it into a interface, but i managed to mix it into a FileInputStream, if that suits your case. I guess you can add state using the MetaClass which comes in the constructor.
To write a class to be mixed into InputStream. It needs to be:
In the package groovy.runtime.metaclass.java.io
Named FileInputStreamMetaClass (exactly)
Compiled and put into the classpath
Extend DelegatingMetaClass
It can only intercept the GroovyObject methods, so it is not so straightforward. If you are in for a pure dynamic groovy, it is great:
package groovy.runtime.metaclass.java.io
class FileInputStreamMetaClass extends DelegatingMetaClass {
FileInputStreamMetaClass(MetaClass meta) {
super(meta)
println "built FileInputStreamMetaClass"
}
Object invokeMethod(Object delegate, String method, Object[] args) {
switch (method) {
case "copy":
return "i'm copying stuff"
default:
return super.invokeMethod(delegate, method, args)
}
}
}
Compiling:
$ groovyc FileInputStreamMetaClass.groovy
$ groovy -cp . InputTest.groovy
A test:
InputStream input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "i'm copying stuff"
A bit cumbersome.
I'd go for Extensions any time of the day. Three files:
// file META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
moduleName=InputExtensionModule
moduleVersion=0.1
extensionClasses=InputStreamExtension
The extension:
class InputStreamExtension {
static String copy(InputStream input) {
"copying stuff, doc"
}
}
The test:
def input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "copying stuff, doc"
Compile and run:
$ groovyc InputStreamExtension.groovy
$ groovy ISExtensionTest.groovy
And i think the extension is the perfect place to use the static { mixin } block. With some changes:
class InputStreamExtension {
static {
InputStream.mixin InputStreamMixin
}
static String copy(InputStream input) { "copying stuff, doc" }
}
#Category(InputStream)
class InputStreamMixin {
Object pop() {
"input mixin pop"
}
}
A new test:
def input = new FileInputStream("/tmp/test.tmp")
assert input.copy() == "copying stuff, doc"
assert input.pop() == "input mixin pop"
Well I finally figured it out on my own. Essentially the this reference refers to the instance of the Mixin which doesn't do us much good. However, you can use the "as" keyword to convert that to the instance of the target class. For example:
class MyMixin {
static {
File mixin MyMixin
}
File foo() {
return this as File
}
}
File f = new File()
println( f.foo().equals( f ) )
As for mixinOwner and owner references that the jira bug refers to. They don't exist. This is the only way to get a reference to the instance that the mixin was added to.
I wrote up a longer blogpost about it because I thought this was important information for future Groovy programmers since there is zero official docs discussing this.
http://wrongnotes.blogspot.com/2013/06/groovy-mixins-and-this-pointer.html
I am glad you asked this question. To answer a very important question:
I suppose I really want just a compile time Mixin where I can define the #Mixin on the source of the mixin instead of destination because I didn't write the destination.
You cannot achieve this by #Mixin but we do have something in Groovy which will help you out. It is called #Category. Let me go through your example again to show you how you can actually effectively use this in category. Have a look at the below script:
#Category(InputStream)
class InputStreamCategory{
def copy(OutputStream out){
long total = 0
byte[] buffer = new byte[8096]
int len
while ((len = this.read(buffer)) >= 0) {
out.write(buffer, 0, len)
total += len
}
out.flush()
return this
}
}
class MyUtil{
def str = 'This is a dummy String!!!!'
InputStream inputS = new ByteArrayInputStream(str.bytes)
OutputStream outputS = new ByteArrayOutputStream()
def copy(){
use(InputStreamCategory){
inputS.copy(outputS)
println "Printing Output Stream: " + outputS
}
}
}
def util = new MyUtil()
util.copy()
//Prints:
Printing Output Stream: This is a dummy String!!!!
Explanation:-
#Category(InputStream) sets the this in InputStreamCategory and in your util class you just use the newly added method copy to InputStream. The benefit for using category is that you get hold of the caller object in this case inputS. The first parameter passed into a category always refers to the caller. You can have different categories for different implementations like FileCategory etc and then create an Utility Class to use those Categories. Therefore, you would end up with utilities like zip, copy, etc.
You could get detail information about the same from the api. I also highly recommend going through Category and Mixin Transformations.
To answer the first question:-
do dynamic Mixins replace the use of using metaClass + Closure to create mixins?
No they do not. metaClass implementation does not create a Mixin. It just adds another method in the metaData registry about the class while runtime. You get an handle to the delegate. On the other hand #Mixin gives you the ability to inherit pre-defined properties.

Resources