I would like to execute a function (with parameters) through an annotation tag in a groovy script.
If we execute a method in our groovy script with this annotation it would print in the console (stderr) a custom message like:
warning: '<function_name>' is deprecated [[Use '<Deprecated.instead>' instead.][More info: '<Deprecated.more_info>']]
So, I have created a custom annotation like this
public #interface Deprecated {
public String instead() default null
public String more_info() default null
}
The goal is to use it like this:
def new_call() {
//new version of the method
}
#Deprecated(instead="new_call")
def call() {
//do something
}
In my example, it would output like this:
warning: 'call' is deprecated. Use 'new_call' instead.
I saw this post Groovy: How to call annotated methods, it's over 7 years old now but looks good so i'll look deeper.
I saw also Delegate.deprecated but i'm not sure if that's what i want
I'm not sure I am doing right. So if you have any advice or suggestions, I'll be happy to hear you.
Simple AOP Approach
This is very-very basic implementation with groovy out-of the box.
Deprecated Annotation
#Target([ElementType.METHOD])
#Retention(RetentionPolicy.RUNTIME)
#interface Deprecated {
String instead() default 'null'
String more_info() default 'null'
}
Class which should get this functionality
The class has to implement GroovyInterceptable - invokeMethod.
class SomeClass implements GroovyInterceptable {
#Override
def invokeMethod(String name, args) {
DeprecatedInterception.apply(this, name, args)
}
def new_call() {
println('new_call invoked')
}
#Deprecated(instead = 'new_call', more_info = '... the reason')
def depr_call() {
println('depr_call invoked')
}
}
Interception Util
import org.codehaus.groovy.reflection.CachedMethod
class DeprecatedInterception {
static apply(Object owner, String methodName, Object args) {
MetaMethod metaMethod = owner.metaClass.getMetaMethod(methodName, args)
Deprecated d = extractAnnotation(metaMethod)
if (d) {
println("warning: '$methodName' is deprecated. Use '${d.instead()}' instead. More info: '${d.more_info()}'")
}
// handle methods with var-args
metaMethod.isVargsMethod() ?
metaMethod.doMethodInvoke(owner, args) :
metaMethod.invoke(owner, args)
}
static Deprecated extractAnnotation(MetaMethod metaMethod) {
if (metaMethod instanceof CachedMethod) {
metaMethod.getCachedMethod()?.getAnnotation(Deprecated)
} else {
null
}
}
}
Simple Test
Just check that no exceptions/errors..
class TestWarnings {
#Test
void test() {
new SomeClass().with {
new_call()
depr_call()
}
}
}
Output:
new_call invoked
warning: 'depr_call' is deprecated. Use 'new_call' instead. More info: '... the reason'
depr_call invoked
Disclaimer
This should work for most cases, but has some limitations:
will not work for static methods (unless invoked on Object instance)
you have to implement GroovyInterceptable per each class, to apply
you might faced with some side-effects in some groovy syntax or features (at least I've found the issue with vararg methods invocation, but this already fixed)
So this should be tested and potentially improved before widely using for some production projects.
Other options:
Shortly, because implementation might be more complex (not sure, at least I not able to provide some example in a short time), but potentially this is more solid.
Adding AST Transformations.
Use some AOP library.
Related
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:
A simplified version of what I'm trying to do in Groovy:
class Animal {
static def echo() {
println this.name // ie "class.name"
}
}
class Dog extends Animal {
}
class Cat extends Animal {
}
Dog.echo()
Cat.echo()
// Output:
// => Animal
// => Animal
//
// What I want:
// => Dog
// => Cat
I think what I'm asking here is: when I call a static method on an object, and
the static method is defined in the object's superclass, is there a way to obtain
the actual type of the object?
A static method is not defined in the object context, but in the class context. You might get confused by the presence of this in the Groovy static method. However, it's only a syntactic sugar that eventually replaces this.name with Animal.class.name.
If you compile the Animal class from your example with a static compilation enabled, you will see that it compiles to the following Java equivalent (result after decompiling the .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.DefaultGroovyMethods;
public class Animal implements GroovyObject {
public Animal() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static Object echo() {
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
return null;
}
}
You can see that the following line in the echo method:
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
operates directly on the Animal class name. So from the echo method perspective, it doesn't matter how many classes extend it. As long as those classes invoke echo method defined in the Animal class, you will always see Animal printed as a result.
And there is even more than that. If you use the following compiler configuration script:
config.groovy
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
ast(groovy.transform.TypeChecked)
}
and then compile the script (let's call it script.groovy) using this configuration option with the following command:
groovyc --configscript=config.groovy script.groovy
then you will see something like this after decompiling the .class file:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import org.codehaus.groovy.runtime.InvokerHelper;
public class script extends groovy.lang.Script {
public script() {
}
public script(Binding context) {
super(context);
}
public static void main(String... args) {
InvokerHelper.runScript(script.class, args);
}
public Object run() {
Animal.echo();
return Animal.echo();
}
}
You can see that even though you have invoked Dog.echo() and Cat.echo() in your Groovy script, the compiler replaced these calls with the double Animal.echo() invocation. It happened because calling this static method on any other subclass does not make any difference.
Possible solution: applying double dispatch
There is one way to get the expected output - override echo static method in Dog and Cat class. I can assume that your real method may do something more than the exemplary echo method you have shown above, so you might need to call the super echo method from a parent class. But... there are two problems: (1) you can't use super.echo() in the static context, and (2) it doesn't solve the problem, because parent method still operates in the Animal class context.'
To solve this kind of issue you might want to mimic a technique called double dispatch. In short - when we don't have information about the caller in the method that was called, let's allow the caller to pass this information with the method call. Consider the following example:
import groovy.transform.CompileStatic
#CompileStatic
class Animal {
// This is a replacement for the previous echo() method - this one knows the animal type from a parameter
protected static void echo(Class<? extends Animal> clazz) {
println clazz.name
}
static void echo() {
echo(Animal)
}
}
#CompileStatic
class Dog extends Animal {
static void echo() {
echo(Dog)
}
}
#CompileStatic
class Cat extends Animal {
static void echo() {
echo(Cat)
}
}
Animal.echo()
Dog.echo()
Cat.echo()
This may sound like a boilerplate solution - it requires implementing echo method in each subclass. However, it encapsulates the echo logic in the method that requires Class<? extends Animal> parameter, so we can let every subclass to introduce their concrete subtype. Of course, this is not a perfect solution. It requires implementing echo method in each subclass, but there is no other alternative way. Another problem is that it doesn't stop you from calling Dog.echo(Animal) which will cause the same effect as calling Animal.echo(). This double dispatch like approach is more like introducing a shorthand version of echo method which uses the common static echo method implementation for simplicity.
I don't know if this kind of approach solves your problem, but maybe it will help you find a final solution.
I'm coding in Groovy and am having trouble with the Java 8 #Repeatable meta-annotation. I think I'm doing everything right, but it appears that Groovy is not recognizing #Repeatable. Here's my sample code; I'm expecting the information from both annotations to get stored in MyAnnotationArray:
import java.lang.annotation.*
class MyClass
{
#MyAnnotation(value = "val1")
#MyAnnotation(value = "val2")
void annotatedMethod()
{
println("annotated method called")
}
public static void main(String... args)
{
MyClass ob = new MyClass()
ob.annotatedMethod()
java.lang.reflect.Method m = ob.getClass().getMethod("annotatedMethod")
List annos = m.getAnnotations()
println("annos = $annos")
}
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Repeatable(MyAnnotationArray)
public #interface MyAnnotation
{
String value() default "val0";
}
public #interface MyAnnotationArray
{
MyAnnotation[] MyAnnotationArray()
}
What happens is that I get this error:
Caught: java.lang.annotation.AnnotationFormatError: Duplicate annotation for class: interface MyAnnotation: #MyAnnotation(value=val2)
java.lang.annotation.AnnotationFormatError: Duplicate annotation for class: interface MyAnnotation: #MyAnnotation(value=val2)
Which is exactly what I get if I leave out the #Repeatable meta-annotation.
The code works fine if I leave out one of the duplicate MyAnnotations; then there is no error, and I then can read the annotation value as expected.
Is it possible that Groovy doesn't support the #Repeatable meta-annotation? I couldn't find any documentation that states this outright, though this page hints that maybe this is the case (scroll down to item 88).
seems to be not supported
i used java 1.8 and groovy 2.4.11
after compiling and de-compilig the same code i got this:
java:
#MyAnnotationArray({#MyAnnotation("val1"), #MyAnnotation("val2")})
public void annotatedMethod()
{
System.out.println("annotated method called");
}
groovy:
#MyAnnotation("val1")
#MyAnnotation("val2")
public void annotatedMethod()
{
System.out.println("annotated method called");null;
}
so, as workaround in groovy use
//note the square brackets
#MyAnnotationArray( [#MyAnnotation("val1"), #MyAnnotation("val2")] )
public void annotatedMethod()
{
System.out.println("annotated method called");
}
full script (because there were some errors in annotation declaration)
import java.lang.annotation.*
class MyClass
{
//#MyAnnotation(value = "val1")
//#MyAnnotation(value = "val2")
#MyAnnotationArray( [#MyAnnotation("val1"), #MyAnnotation("val2")] )
public void annotatedMethod()
{
System.out.println("annotated method called");
}
public static void main(String... args)
{
MyClass ob = new MyClass()
ob.annotatedMethod()
java.lang.reflect.Method m = ob.getClass().getMethod("annotatedMethod")
List annos = m.getAnnotations()
println("annos = $annos")
}
}
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
#Repeatable(MyAnnotationArray)
public #interface MyAnnotation
{
String value() default "val0";
}
#Retention(RetentionPolicy.RUNTIME)
public #interface MyAnnotationArray
{
MyAnnotation[] value()
}
also tried against groovy 3.0.0-SNAPSHOT - the result is the same as for 2.4.11
Yes, Groovy has supported "repeatable" annotations for a long time even in Java 5 so long as retention policy was only SOURCE. This is what allows multiple #Grab statements for instance without the outer #Grapes container annotation. Being only retained in SOURCE makes them useful for AST transformations and within the Groovy compiler itself (and other source processors) but not really useful anywhere else. We don't currently support #Repeatable at all but plan to in a future version.
My code as below, refering to the solution in https://stackoverflow.com/a/30308199/3286489
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.*
class SimpleClassTest {
private fun <T> anyObject(): T {
Mockito.anyObject<T>()
return uninitialized()
}
private fun <T> uninitialized(): T = null as T
lateinit var simpleObject: SimpleClass
#Mock lateinit var injectedObject: InjectedClass
#Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
#Test
fun testSimpleFunction() {
simpleObject = SimpleClass(injectedObject)
verify(injectedObject).settingDependentObject(anyObject())
}
}
I still have the below error
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method my.package.InjectedClass.settingDependentObject, parameter dependentObject
Did I miss anything?
UPDATED
Below is the code tested (simplest form and working)
class SimpleClass(val injectedClass: InjectedClass) {
fun simpleFunction() {
injectedClass.settingDependentObject(DependentClass(Response.Builder().build()))
}
}
open class DependentClass(response: Response) {
}
open class InjectedClass() {
lateinit var dependentObject: DependentClass
fun settingDependentObject(dependentObject: DependentClass) {
this.dependentObject = dependentObject
}
}
By default Kotlin classes and members are final. Mockito cannot mock final classes or methods.
Thus when you write:
verify(injectedObject).settingDependentObject(anyObject())
the real implementation is called which requires non null argument.
To fix that either open your class and method or, even better, change SimpleClass to accept an interface as its constructor argument and mock the interface instead.
There is a project specifically to help deal with Kotlin "closed by default" in unit testing with Mockito. For JUNIT, you can use the kotlin-testrunner which is an easy way to make any Kotlin test automatically open up classes for testing as they are loaded by the classloader. Usage is simple, just add one annotation of #RunWith(KotlinTestRunner::class), for example:
#RunWith(KotlinTestRunner::class)
class MyKotlinTestclass {
#Test
fun test() {
...
}
}
This is thoroughly covered in the article Never say final: mocking Kotlin classes in unit tests
This covers your use case in an automatic way by allowing all classes to be mocked that otherwise would not be allowed.
I ran into the same issue with Mockito when using RETURNS_DEEP_STUBS. It seems like nulls are still returned for nested objects, even when using the kotlin-allopen plugin.
Please check out and comment on this issue on Mockito if you're having the same problem.
You can use this function instead
inline fun <reified T : Any> any(): T = Mockito.any(T::class.java) ?: T::class.java.newInstance()
If the following is entered in Eclipse/STS (with groovy):
interface iFaceWithAnIssue {
def thisIsFine(a,b,c)
def thisHasProblems(alpha='va')
}
The only line that complains is the one trying to use a default value. I can not tell from the codehaus site if this is supported or not.
The IDE error is:
Groovy:Cannot specify default value for method parameter
So this makes me think it is not supported. As there will be multiple implementations, I wanted to use an interface here. I don't really need the default value in the interface, but there is an error trying to fulfill the interface contract if the implementation class then tries to default this argument. Is there any way?
No, you cannot.
When you define a default value, Groovy actually creates multiple methods in your class, so for example:
class Test {
void something( a=false ) {
println a
}
}
Actually creates
public void something(java.lang.Object a) {
this.println(a)
}
and
public void something() {
this.something(((false) as java.lang.Object))
}
This can't be done as it stands in Interfaces.
You could do:
interface iFaceWithAnIssue {
def thisHasProblems()
def thisHasProblems(alpha)
}
Then
class Test implements iFaceWithAnIssue {
// This covers both Inteface methods
def thisHasProblems(alpha='va') {
// do something
}
}