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

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'
}

Related

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.

modify script variable from a Closure in Groovy

I am trying to modify a script variable from inside a closure in a function. The problem can be distilled down to this:
#groovy.transform.Field int myField = 0
incrementField()
assert myField == 1
def incrementField() {
1.times { myField++ }
}
I think the problem has something to do with closure delegates, but I cannot quite wrap my head around the docs.
This behavior is caused by groovy.lang.Script class and the fact that it overrides following methods:
Object getProperty(String property)
void setProperty(String property, Object newValue)
Closure you have shown in the example uses delegate set to a script object and that's why both overridden methods get executed when you try to access or modify field defined in a script.
Now let's see what happens when your example reaches closure
{ myField++ }
Firstly, getProperty("myField") is called to return a value associated with this property. This method is implemented as:
public Object getProperty(String property) {
try {
return binding.getVariable(property);
} catch (MissingPropertyException e) {
return super.getProperty(property);
}
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Script.java#L54
binding object contains only one variable in the beginning - closure's args array. If we take a look at implementation of binding.getVariable(property) method we will see:
public Object getVariable(String name) {
if (variables == null)
throw new MissingPropertyException(name, this.getClass());
Object result = variables.get(name);
if (result == null && !variables.containsKey(name)) {
throw new MissingPropertyException(name, this.getClass());
}
return result;
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Binding.java#L56
In our case MissingPropertyException is being thrown, so Script.getProperty(property) method returns a value of field myField defined in our Groovy script - 0. Then Groovy increments this value by 1 and tries to set this new value to a field myField. In this case Script.setProperty(property, value) is being called:
public void setProperty(String property, Object newValue) {
if ("binding".equals(property))
setBinding((Binding) newValue);
else if("metaClass".equals(property))
setMetaClass((MetaClass)newValue);
else
binding.setVariable(property, newValue);
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/Script.java#L62
As you can see it sets this new value using bindings object. If we display binding.variables we will see that now this internal map contains two entries: args -> [:] and myField -> 1. It explains why assertion in your script always fails. Body of the closure you have defined never reaches myField field from the script class.
Workaround
If you are not satisfied with the fact that Script class overrides setProperty(property, value) method you can always override it by hand in your script and use same implementation as GroovyObjectSupport.setProperty(property, value). Simply add below method to your Groovy script:
#Override
void setProperty(String property, Object newValue) {
getMetaClass().setProperty(this, property, newValue)
}
Now closure defined in incrementField will set a new value to a class field instead of to a bindings object. Of course it may cause some weird side effects, you have to be aware of that. I hope it helps.
Found a possible solution, using closure delegate:
#groovy.transform.Field def stuff = [
myField : 0
]
incrementField()
assert stuff.myField == 1
def incrementField() {
def body = { myField++ }
body.resolveStrategy = Closure.DELEGATE_FIRST
body.delegate = stuff
1.times body
}

Groovy Copying / Combining MetaMethods From Multiple Objects

I have two classes. At runtime, I want to "clone" the methods of one object, over to another. Is this possible? My failed attempt using leftshift is shown below.
(Note: I also tried currMethod.clone() with the same result.)
class SandboxMetaMethod2 {
String speak(){
println 'bow wow'
}
}
class SandboxMetaMethod1{
void leftShift(Object sandbox2){
sandbox2.metaClass.getMethods().each{currMethod->
if(currMethod.name.contains("speak")){
this.speak()
this.metaClass."$currMethod.name" = currMethod
this.speak()
}
}
}
String speak(){
println 'woof'
}
}
class SandboxMetaMethodSpec extends Specification {
def "try this"(){
when:
def sandbox1 = new SandboxMetaMethod1()
def sandbox2 = new SandboxMetaMethod2()
sandbox1 << sandbox2
then:
true
}
}
//Output
woof
speak
woof
Per Request, I am adding background as to the goal / use case:
It's very much like a standard functional type of use case. In summary, we have a lot of methods on a class which applies to all of our client environments (50-100). We apply those to process data in a certain default order. Each of those methods may be overridden by client specific methods (if they exist with the same method name), and the idea was to use the approach above to "reconcile" the method set. Based on the client environment name, we need a way to dynamically override methods.
Note: Overriding methods on the metaclass is very standard (or should i say, it's the reason the amazing capability exists). And it works if my method exists as text like String currMethod = "{x-> x+1}", then i just say this.metaClass."$currMethodName" = currMethod. My challenge in this case is that my method is compiled and exists on another class, rather than being defined as text somewhere.
The goal of having all the custom methods compiled in client-specific classes at build time was to avoid the expense of compilation of these dynamic methods at runtime for each calculation, so all client-specific methods are compiled into a separate client-specific JAR at build time. This way also allows us to only deploy the client-specific code to the respective client, without all the other clients calculations in some master class.
I hope that makes sense.
New Approach, in Response to Jeremie B's suggestion:
Since I need to choose the trait to implement by name at runtime, will something like this work:
String clientName = "client1"
String clientSpeakTrait = "${clientName}Speak"
trait globalSpeak {
String speak() {
println 'bow wow'
}
}
trait client1Speak {
String speak() {
println 'woof'
}
}
def mySpeaker = new Object().withTraits globalSpeak, clientSpeakTrait
A basic example with Traits :
trait Speak {
String speak() {
println 'bow wow'
}
}
class MyClass {
}
def instance = new MyClass()
def extended = instance.withTraits Speak
extended.speak()
You can choose which trait to use at runtime :
def clientTrait = Speak
def sb = new Object().withTraits(clientTrait)
sb.speak()
And dynamically load the trait with a ClassLoader :
def clientTrait = this.class.classLoader.loadClass "my.package.${client}Speak"
def sb = new Object().withTraits(clientTrait)

How to restore metaclass on object to original class definition

I've been trying to create a TEMPORARY override on new objects, and then to remove the override on the objects themselves. I'm not sure if this can be done, but here is what I've tried so far.
// Say I have a class like:
class Validator {
boolean validate() { println "code here to return actual true/false"; false }
}
// I have two integration points one of them is Here before construction:
// First integration point:
// Save actual validate function
def realValidate = Validator.&validate
// Make new instances of Validator have the validate function hardwired to true
Validator.metaClass.validate { -> println "hardwired true"; true }
// Code I'd rather not modify
// Now some code executes which news up an instance and calls validate
def validator = new Validator()
validator.validate() // This correctly calls our override
// Second integration point.
// Without newing up a new Validator object, I'd like to remove the override.
Validator.metaClass = null
validator.metaClass.validate = Validator.&validate
// This throws "java.lang.IllegalArgumentException: object is not an instance of declaring class"
//validator.validate()
// So maybe I have to explicitly say:
realValidate.resolveStrategy = Closure.DELEGATE_FIRST
// But this still throws the same exception
//validator.validate()
// Perhaps if I tell my objects metaclass to forget about validate, it will bubble up and look for the method on its declaring class?
validator.metaClass.validate = { -> throw new MissingMethodException("validate", Validator.class, (Object[])[], false) }
// This throws MissingMethodException: No signature of method: Validator.validate() is applicable for argument types: () values: []
// Possible solutions: validate(), wait()
//validator.validate()
Apologies for not having a super specific question, since I don't know what all is possible in this particular area. I'd love both the reason why my code doesn't work, as well as alternatives to make it work.
This could be a per instance meta class problem... Validator.metaClass = null will set the global meta class for the Validator class to default. but your validator instance here is a Groovy class and thus stores a separate reference to the meta class in the instance itself. Calls with that instance will not go through a lookup of the global meta class and instead use the per instance meta class (the reference stored in the instance itself). Thus validator.metaClass = null is the only way to reset this
A small modification to your strategy would be fruitful. Use metaClass on the object instead of the Class.
// Say I have a class like:
class Validator {
boolean validate() { println "code here to return actual true/false"; false }
}
def validator = new Validator()
// mark that the pointer is on object instead of class
def realValidate = validator.&validate
validator.metaClass.validate { -> println "hardwired true"; true }
validator.validate() // This correctly calls our override
// Second integration point.
// DO NOT NEED THIS
// validator.metaClass = null
// Assign the method pointer to validate to call original validate
validator.metaClass.validate = realValidate
validator.validate()
Your approach did not work because you had validate() overridden on the metaClass of Class reference instead of the object itself.

intercepting LOCAL property access in 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

Resources