How would I write a Pointcut for methods taking any number of arguments that either implements Serializable or are of primitive type?
E.g. Pointcut should match:
methodA(String str, int i)
methodB(String str, String str2, List<String> list)
but not
methodC(String str, DataX data)
where DataX is not Serializable
There is no way I know of to specify that kind of condition in a pointcut, so the only thing I have for you is quite expensive because it involves runtime checks - but it does what you want:
Driver application:
package de.scrum_master.app;
import java.io.Serializable;
public class Application {
public static class MyNonSerializable {}
public static class MySerializable implements Serializable {
private static final long serialVersionUID = 1L;
}
private static MyNonSerializable one() {
return new MyNonSerializable();
}
static MySerializable two(int i, String string) {
return new MySerializable();
}
static String three(int i, String string, MyNonSerializable myNonSerializable) {
return "foo";
}
static MyNonSerializable four(int i, String string, MySerializable mySerializable) {
return new MyNonSerializable();
}
static void five(MyNonSerializable myNonSerializable) {
}
static int six(MySerializable mySerializable) {
return 11;
}
public static void main(String[] args) {
one();
two(11, "foo");
three(11, "foo", new MyNonSerializable());
four(11, "foo", new MySerializable());
five(new MyNonSerializable());
six(new MySerializable());
}
}
Now, regardless of the return type, we want to intercept executions with either zero arguments or any number of Serializable
arguments. The methods conforming to this condition are main
, one
, two
, four
, six
.
Aspect with runtime type checking:
package de.scrum_master.aspect;
import java.io.Serializable;
public aspect MethodsWithSerializableArgumentsAspect {
before() : execution(* *(..)) {
boolean nonSerializableArgumentFound = false;
for (Object arg : thisJoinPoint.getArgs()) {
if (!(arg == null || arg instanceof Serializable)) {
nonSerializableArgumentFound = true;
break;
}
}
if (nonSerializableArgumentFound)
return;
System.out.println(thisJoinPoint);
}
}
Console log:
execution(void de.scrum_master.app.Application.main(String[]))
execution(Application.MyNonSerializable de.scrum_master.app.Application.one())
execution(Application.MySerializable de.scrum_master.app.Application.two(int, String))
execution(Application.MyNonSerializable de.scrum_master.app.Application.four(int, String, Application.MySerializable))
execution(int de.scrum_master.app.Application.six(Application.MySerializable))
Tadaa! :-)
Now if you also want to exclude methods returning non-serializable objects, that should limit the output to main
, two
, six
. You can do it like this:
package de.scrum_master.aspect;
import java.io.Serializable;
public aspect MethodsWithSerializableArgumentsAspect {
Object around() : execution(* *(..)) {
boolean nonSerializableArgumentFound = false;
for (Object arg : thisJoinPoint.getArgs()) {
if (!(arg instanceof Serializable)) {
nonSerializableArgumentFound = true;
break;
}
}
Object result = proceed();
if ((result == null || result instanceof Serializable) && !nonSerializableArgumentFound)
System.out.println(thisJoinPoint);
return result;
}
}
Console log:
execution(Application.MySerializable de.scrum_master.app.Application.two(int, String))
execution(int de.scrum_master.app.Application.six(Application.MySerializable))
execution(void de.scrum_master.app.Application.main(String[]))
Please note that in order to do a runtime check on the return value, we can only print the result after the method has terminated. Thus, main
is at the end of the log output, not at the beginning as in the first solution.
Update: I forgot to explain why we need to check aruments and return value for null
. This is because we want to accept null
or void
as Serializable
, but actually null instanceof Serializable
is false.
Update 2 - static pointcut solution without runtime type checks:
Okay, I was just eating an apple (no, it did not fall onto my head) and suddenly had this idea. It actually works nicely and is exactly what you want:
package de.scrum_master.aspect;
import java.io.Serializable;
public aspect MethodsWithSerializableArgumentsAspect {
pointcut returnsSerializable() :
execution((Serializable+ || byte || short || int || long || float || double || char || boolean || void) *(..));
pointcut hasNonSerializableArguments() :
execution(* *(.., !(Serializable+ || byte || short || int || long || float || double || char || boolean || void), ..));
before() : returnsSerializable() && !hasNonSerializableArguments() {
System.out.println(thisJoinPoint);
}
}
BTW, Serializable+
means "Serializable and all of its subclasses or implementing classes".
Enjoy!