aboutsummaryrefslogtreecommitdiffstats
path: root/docs/progGuideDB/pitfalls.xml
blob: 5379d5daa15ee9ed911e23f6db7b094951b1a72d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<chapter id="pitfalls" xreflabel="Pitfalls">
  <title>Pitfalls</title>

  <sect1><!-- About this Chapter -->
    <title>About this Chapter</title>

    <para>This chapter consists of aspectj programs that may lead to surprising
     behaviour and how to understand them. 
    </para>

  </sect1>

  <sect1>
    <title>Infinite loops</title>

    <para>Here is a Java program with peculiar behavior </para>

<programlisting><![CDATA[
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();
        }
    }
}
]]></programlisting>

    <para>This program will never reach the println call, but when it aborts
    will have no stack trace. </para>

    <para>This silence is caused by multiple StackOverflowExceptions.  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.  </para>

    <para> The following short aspect will also generate this behavior:
    </para>

<programlisting><![CDATA[
aspect A {
    before(): call(* *(..)) { System.out.println("before"); }
    after():  call(* *(..)) { System.out.println("after"); }
}
]]></programlisting>

    <para>Why?  Because the call to println is also a call matched by the
    pointcut <literal>call (* *(..))</literal>. We get no output because we
    used simple after() advice.  If the aspect were changed to</para>

<programlisting><![CDATA[
aspect A {
    before(): call(* *(..)) { System.out.println("before"); }
    after() returning:  call(* *(..)) { System.out.println("after"); }
}
]]></programlisting>

    <para>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.  </para> 

    <para>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: </para>

<programlisting><![CDATA[
aspect A {
    before(): call(* *(..)) && !within(A) { System.out.println("before"); }
    after() returning:  call(* *(..)) && !within(A) { System.out.println("after"); }
}
]]></programlisting>

    <para>Other solutions might be to more closely restrict the pointcut in
    other ways, for example:  </para>

<programlisting><![CDATA[
aspect A {
    before(): call(* MyObject.*(..))  { System.out.println("before"); }
    after() returning:  call(* MyObject.*(..))  { System.out.println("after"); }
}
]]></programlisting>

    <para>The moral of the story is that unrestricted generic pointcuts can
    pick out more join points than intended. </para>

  </sect1>
</chapter>

<!--
Local variables:
compile-command: "java sax.SAXCount -v progguide.xml && java com.icl.saxon.StyleSheet -w0 progguide.xml progguide.html.xsl"
fill-column: 79
sgml-local-ecat-files: "progguide.ced"
sgml-parent-document:("progguide.xml" "book" "chapter")
End:
-->