How to override method Class<?>.getAt in Groovy - groovy

Currently I'm using Groovy 1.8.2 and the following code works for me as expected:
Class.metaClass.getAt = { args ->
println "Called ${delegate}[${args}]"
TypeDefinition.create(delegate, args)
}
I use that in my DSL as shown:
TypeDefinition instance = List[MyOwnClass]
When I moved to Groovy 2.0.5 this functinality failed with "Missing method: static java.util.List.getAt() with parameter some.package.MyOwnClass". So the question is how can I make it work with Groovy 2?

Using a category works with 2.0.5:
class ClassHelperCategory {
static getAt(Class cls, String arg) {
"Called $cls[$arg]"
}
}
Class.metaClass.mixin(ClassHelperCategory)
assert List['hello'] == 'Called interface java.util.List[hello]'

Related

Why is the return type in Function<T, R> ignored in Groovy?

The following simple code explains my confusion:
class Main {
static void f(Function<Float, Float> c) {
println(c.apply(0.0f))
}
static void main(String[] args) {
Closure<String> c = {"hi"}
f(c)
}
}
I have no idea why the compiler does not complain that Closure<String> is not appropriate for Function<Float, Float>. Seems that I can pass anything to f().
the following code
import java.util.function.*
def c = {"result $it :: ${it.getClass()}"}
Function<Float, Float> f = c
println "f: ${f.getClass()} ${f instanceof Function}"
println "c: ${c.getClass()} ${c instanceof Function}"
println f.apply(0.1)
prints
f: class com.sun.proxy.$Proxy22 true
c: class ConsoleScript10$_run_closure1 false
result 0.1 :: class java.math.BigDecimal
groovy is dynamic - there is no type check unless you specify this (CompileStatic)
closure does not implement function. so, when you are assigning closure into function - groovy tries to delegate closure through a function interface. it will be dynamic even if you use compile static...

Riddle me this: Inconsistent groovy meta programming behaviour

I stumbled across this when updating a large app from groovy 2 to 3 (and also to corresponding newer spock and geb versions).
This code behaves strange and also a different kind of strange in groovy 2 versus groovy 4.
I think we are running without "indy" here. I guess because all the transitive dependencies of our large app bring in specific groovy jars without indy. I should probably goe through them carefully and adapt our gradle build so that only "indy" versions of all jars are picked.
class A {
def foo() {
bar('hello')
beep(Optional.of('hello'))
}
protected void bar(String value) { println 'A.bar' }
protected void beep(Optional<String> value) { println 'A.beep' }
}
class B extends A {
protected void bar(String value) { println 'B.bar' }
protected void beep(Optional<String> value) { println 'B.beep' }
}
class C extends B implements GroovyInterceptable {
def invokeMethod(String name, Object args) {
super."$name"(*args)
}
}
static void main(String[] args) {
new C().foo()
println '---'
C c = new C()
c.bar('hello')
c.beep(Optional.of('hello'))
}
Output for groovy 2.5.15:
B.bar
A.beep
---
A.bar
A.beep
Output for groovy 4.0.0:
A.bar
A.beep
---
A.bar
A.beep
What I would have expected:
B.bar
B.beep
---
B.bar
B.beep
What's going on here? Bug or some strange, but expected corner case?
Where is the difference in behavior in between groovy 2 and 4 documented?
In our real app there was a difference already in between groovy 2 and 3 but I have been unable so far to create example code for that.
Is there a way to call the original method inside of invokeMethod? (Can't find anything in the docs, which are very sparse btw.)
I get your 3.0.9 output for Groovy 2.5.16, 3.0.10 and 4.0.1 -- indy enabled for all three.
Your implementation of invokeMethod relies on the behavior of ScriptBytecodeAdapter#invokeMethodOnSuperN which is what is behind super."$name"(*args). When handling "bar" message, the meta-method index has B.bar(java.lang.String) for "this" and B.super$2$bar(java.lang.String) for "super". super$2$bar is a meta-object protocol (MOP) method that provides the necessary INVOKESPECIAL instruction to reach A#bar(java.lang.String).
If you want the output of all calls to be from B then you can use this."$name"(*args) instead. In your specific case, there is no need to implement C as GroovyInterceptable and to try and route "foo", "bar" and "beep" yourself.
You can make your code produce the expected output by making the B class compiled statically:
import groovy.transform.CompileStatic
class A {
def foo() {
bar('hello')
beep(Optional.of('hello'))
}
protected void bar(String value) { println 'A.bar' }
protected void beep(Optional<String> value) { println 'A.beep' }
}
#CompileStatic
class B extends A {
protected void bar(String value) { println 'B.bar' }
protected void beep(Optional<String> value) { println 'B.beep' }
}
class C extends B implements GroovyInterceptable {
def invokeMethod(String name, Object args) {
super."$name"(*args)
}
}
static void main(String[] args) {
new C().foo()
println '---'
C c = new C()
c.bar('hello')
c.beep(Optional.of('hello'))
}
Output:
B.bar
B.beep
---
B.bar
B.beep
As it was mentioned by emilies in his answer, in the MOP use case scenario something like this happens:
c.bar('Hello')
invokeMethod('bar', ['Hello'] as Object[])
super."bar"(['Hello'] as Object[])
This super."bar"(['Hello'] as Object[]) is represented by B.super$2$bar(java.lang.String) method object which forces A.bar(java.lang.String) to be invoked right in the next call frame.
However, if you make the B class to be compiled statically, the method that is found to satisfy the super."bar"(['Hello'] as Object[]) expression, in that case, is B.bar(java.lang.String), and thus it gets invoked directly.
Regarding the differences between Groovy 2.5 and Groovy >=3.0, it looks like you have encountered a compiler bug. The bar('hello') inside the A.foo() method ignores the MOP and goes directly to this.bar(java.lang.String) which in this case is B.bar(java.lang.String).
It looks like it happens for the java.lang.String type (didn't check other types). However, when the type is java.util.Optional, then a call like beep(Optional.of('Hello')) inside the A.foo() method goes through the MOP and thus it discovers B.super$2$beep(java.util.Optional) method to be invoked:

MissingMethodException when using Groovy and JUnit 5 Assertions

Trying to use JUnit 5 class Assertions, specifically assertAll() with Groovy.
Basic Java proves the following syntax:
#Test
public void someTest() {
assertAll('heading',
() -> assertTrue(firstName),
() -> assertTrue(lastName)
);
}
Since I'm stuck to Groovy v2.4, which does not support lambdas, what I do looks like (tried several options, however all of those provide the same error):
#Test
void 'Some test'() {
assertAll('heading',
{ -> assert firstName },
{ -> assert lastName }
)
}
Error message:
groovy.lang.MissingMethodException:
No signature of method: static org.junit.jupiter.api.Assertions.assertAll() is applicable for argument
types: (path.GeneralTests$_Some_test_closure10...)
values: [path.GeneralTests$_Some_test_losure10#1ef13eab, ...]
Looks like the problem is with assertAll() method itself, exception is quite obvious, however that's quite a challenge to get the possible solution, since I had to switch to Groovy from Python just recently.
Indeed, I am going to take some courses with Groovy in future, but you know, you always have to provide results for yesterday.
Would highly appreciate your suggestions!
In this particular case, you need to cast Groovy closure to org.junit.jupiter.api.function.Executable interface explicitly:
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.function.Executable
import static org.junit.jupiter.api.Assertions.*
class Test {
#Test
void 'Some test'() {
assertAll('heading',
{ assertTrue(true) } as Executable,
{ assertFalse(false) } as Executable)
}
}

Confused about the invokeMethod method in the Groovy MOP

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.

Groovy: Got StackOverflowError when override invokeMethod with a closure

I'm trying to enhance my Grails project with Groovy AOP approach. However I always got StackOverflowError if I override invokeMethod with a closure. Here is my test code, I can reproduce the error with groovy 2.1.3, thanks!
class A implements GroovyInterceptable
{
void foo(){
System.out.println( "A.foo");
}
}
class B extends A
{
void foo(){
System.out.println( "B.foo");
super.foo();
}
}
def mc = B.metaClass;
mc.invokeMethod = { String name, args ->
// do "before" and/or "around" work here
try {
def value = mc.getMetaMethod(name, args).invoke(delegate, args)
// do "after" work here
return value // or another value
}
catch (e) {
// do "after-throwing" work here
}
}
B b = new B();
b.foo();
Looks like, if you have a call to super() then metaClass uses the cache to find the method and throws a StackOverflow eventually. In that case if you metaClass A instead of B, it all works fine.
def mc = A.metaClass
I can infer it this way, class implementing GroovyInterceptable directly should override invokeMethod.
#Source MetaClassImpl

Resources