aboutsummaryrefslogtreecommitdiffstats
path: root/docs/progguide/pitfalls.adoc
diff options
context:
space:
mode:
Diffstat (limited to 'docs/progguide/pitfalls.adoc')
-rw-r--r--docs/progguide/pitfalls.adoc91
1 files changed, 91 insertions, 0 deletions
diff --git a/docs/progguide/pitfalls.adoc b/docs/progguide/pitfalls.adoc
new file mode 100644
index 000000000..eb9e15315
--- /dev/null
+++ b/docs/progguide/pitfalls.adoc
@@ -0,0 +1,91 @@
+== Pitfalls
+
+[[pitfalls-intro]]
+=== Introduction
+
+This chapter consists of a few AspectJ programs that may lead to
+surprising behavior and how to understand them.
+
+[[pitfalls-infiniteLoops]]
+=== Infinite loops
+
+Here is a Java program with peculiar behavior
+
+[source, java]
+....
+public class Main {
+ public static void main(String[] args) {
+ foo();
+ System.out.println("done with call to foo");
+ }
+
+ static void foo() {
+ try {
+ foo();
+ } finally {
+ foo();
+ }
+ }
+}
+....
+
+This program will never reach the `println` call, but when it aborts may
+have no stack trace.
+
+This silence is caused by multiple ``StackOverflowException``s. First the
+infinite loop in the body of the method generates one, which the finally
+clause tries to handle. But this finally clause also generates an
+infinite loop which the current JVMs can't handle gracefully leading to
+the completely silent abort.
+
+The following short aspect will also generate this behavior:
+
+[source, java]
+....
+aspect A {
+ before(): call(* *(..)) { System.out.println("before"); }
+ after(): call(* *(..)) { System.out.println("after"); }
+}
+....
+
+Why? Because the call to println is also a call matched by the pointcut
+`call (* *(..))`. We get no output because we used simple `after()`
+advice. If the aspect were changed to
+
+[source, java]
+....
+aspect A {
+ before(): call(* *(..)) { System.out.println("before"); }
+ after() returning: call(* *(..)) { System.out.println("after"); }
+}
+....
+
+then at least a `StackOverflowException` with a stack trace would be seen.
+In both cases, though, the overall problem is advice applying within its
+own body.
+
+There's a simple idiom to use if you ever have a worry that your advice
+might apply in this way. Just restrict the advice from occurring in join
+points caused within the aspect. So:
+
+[source, java]
+....
+aspect A {
+ before(): call(* *(..)) && !within(A) { System.out.println("before"); }
+ after() returning: call(* *(..)) && !within(A) { System.out.println("after"); }
+}
+....
+
+Other solutions might be to more closely restrict the pointcut in other
+ways, for example:
+
+[source, java]
+....
+aspect A {
+ before(): call(* MyObject.*(..)) { System.out.println("before"); }
+ after() returning: call(* MyObject.*(..)) { System.out.println("after"); }
+}
+....
+
+The moral of the story is that unrestricted generic pointcuts can pick
+out more join points than intended.