1
<chapter id="examples" xreflabel="Examples">
2
<title>Examples</title>
4
<sect1><!-- About this Chapter -->
5
<title>About this Chapter</title>
7
<para>This chapter consists entirely of examples of AspectJ use.
9
<!-- ADD THIS IN AGAIN WHEN IT'S TRUE
11
examples have been chosen because they illustrate common AspectJ usage
12
patterns or techniques. Care has been taken to ensure that they also
13
exhibit good style, in addition to being merely syntactically and
18
<para>The examples can be grouped into four categories:</para>
20
<simplelist columns="2" type="horiz">
21
<member><emphasis role="bold">technique</emphasis></member>
22
<member>Examples which illustrate how to use one or more features of the
25
<member><emphasis role="bold">development</emphasis></member>
26
<member>Examples of using AspectJ during the development phase of a
29
<member><emphasis role="bold">production</emphasis></member>
30
<member>Examples of using AspectJ to provide functionality in an
31
application. </member>
33
<member><emphasis role="bold">reusable</emphasis></member>
34
<member>Examples of reuse of aspects and pointcuts.</member>
41
<title>Obtaining, Compiling and Running the Examples</title>
43
<para>The examples source code is part of AspectJ's documentation
44
distribution which may be downloaded from <ulink
45
url="http://aspectj.org/dl">the AspectJ download page</ulink>.</para>
47
<para>Compiling most examples should be straightforward. Go the
48
<filename><replaceable>InstallDir</replaceable>/examples</filename>
49
directory, and look for a <filename>.lst</filename> file in one of the
50
example subdirectories. Use the <literal>-arglist</literal> option to
51
<literal>ajc</literal> to compile the example. For instance, to compile
52
the telecom example with billing, type </para>
55
ajc -argfile telecom/billing.lst
58
<para>To run the examples, your classpath must include the AspectJ run-time
59
Java archive (<literal>aspectjrt.jar</literal>). You may either set
60
the <literal>CLASSPATH</literal> environment variable or use the
61
<literal>-classpath</literal> command line option to the Java
65
(In Unix use a : in the CLASSPATH)
66
java -classpath ".:<replaceable>InstallDir</replaceable>/lib/aspectjrt.jar" telecom.billingSimulation
70
(In Windows use a ; in the CLASSPATH)
71
java -classpath ".;<replaceable>InstallDir</replaceable>/lib/aspectjrt.jar" telecom.billingSimulation
77
<!-- ============================================================ -->
78
<!-- ============================================================ -->
82
<title>Basic Techniques</title>
84
<para>This section presents two basic techniques of using AspectJ, one each
85
from the two fundamental ways of capturing crosscutting concerns: with
86
dynamic join points and advice, and with static introduction. Advice
87
changes an application's behavior. Introduction changes both an
88
application's behavior and its structure. </para>
90
<para>The first example, <xref endterm="sec:JoinPointsAndtjp:title"
91
linkend="sec:JoinPointsAndtjp"/>, is about gathering and using
92
information about the join point that has triggered some advice. The
93
second example, <xref endterm="sec:RolesAndViews:title"
94
linkend="sec:RolesAndViews"/>, concerns changing an existing class
97
<!-- ======================================== -->
99
<sect2 id="sec:JoinPointsAndtjp"><!-- Join Points and thisJoinPoint -->
100
<title>Join Points and <literal>thisJoinPoint</literal></title>
101
<titleabbrev id="sec:JoinPointsAndtjp:title">Join Points and
102
<literal>thisJoinPoint</literal></titleabbrev>
104
<para>(The code for this example is in
105
<filename><replaceable>InstallDir</replaceable>/examples/tjp</filename>.)</para>
107
<para>A join point is some point in the
108
execution of a program together with a view into the execution context
109
when that point occurs. Join points are picked out by pointcuts. When a
110
join point is reached, before, after or around advice on that join
111
point may be run. </para>
113
<para>When dealing with pointcuts that pick out join points of specific
114
method calls, field gets, or the like, the advice will know exactly what
115
kind of join point it is executing under. It might even have access to
116
context given by its pointcut. Here, for example, since the only join
117
points reached will be calls of a certain method, we can get the target
118
and one of the args of the method directly.
121
<programlisting><![CDATA[
122
before(Point p, int x): target(p)
124
&& call(void setX(int)) {
126
System.out.println("Illegal value for x"); return;
131
<para>But sometimes the join point is not so clear. For
132
instance, suppose a complex application is being debugged, and one
133
would like to know when any method in some class is being executed.
134
Then, the pointcut </para>
136
<programlisting><![CDATA[
137
pointcut execsInProblemClass(): within(ProblemClass)
138
&& execution(* *(..));
141
<para>will select all join points where a method defined within the class
142
<classname>ProblemClass</classname> is being executed. But advice
143
executes when a particular join point is matched, and so the question,
144
"Which join point was matched?" naturally arises.</para>
146
<para>Information about the join point that was matched is available to
147
advice through the special variable <varname>thisJoinPoint</varname>,
149
url="../api/org/aspectj/lang/JoinPoint.html"><classname>org.aspectj.lang.JoinPoint</classname></ulink>. This
150
class provides methods that return</para>
152
<itemizedlist spacing="compact">
153
<listitem>the kind of join point that was matched
155
<listitem>the source location of the current join point
157
<listitem>normal, short and long string representations of the
158
current join point</listitem>
159
<listitem>the actual argument(s) to the method or field selected
160
by the current join point </listitem>
161
<listitem>the signature of the method or field selected by the
162
current join point</listitem>
163
<listitem>the target object</listitem>
164
<listitem>the currently executing object</listitem>
165
<listitem>a reference to the static portion of the object
166
representing the current join point. This is also available through
167
the special variable <varname>thisJoinPointStaticPart</varname>.</listitem>
172
<title>The <classname>Demo</classname> class</title>
174
<para>The class <classname>tjp.Demo</classname> in
175
<filename>tjp/Demo.java</filename> defines two methods
176
<literal>foo</literal> and <literal>bar</literal> with different
177
parameter lists and return types. Both are called, with suitable
178
arguments, by <classname>Demo</classname>'s <function>go</function>
179
method which was invoked from within its <function>main</function>
182
<programlisting><![CDATA[
187
public static void main(String[] args){
194
System.out.println(d.bar(new Integer(3)));
197
void foo(int i, Object o){
198
System.out.println("Demo.foo(" + i + ", " + o + ")\n");
202
String bar (Integer j){
203
System.out.println("Demo.bar(" + j + ")\n");
204
return "Demo.bar(" + j + ")";
213
<title>The Aspect <literal>GetInfo</literal></title>
215
<para>This aspect uses around advice to intercept the execution of
216
methods <literal>foo</literal> and <literal>bar</literal> in
217
<classname>Demo</classname>, and prints out information garnered from
218
<literal>thisJoinPoint</literal> to the console. </para>
221
<title>Defining the scope of a pointcut</title>
223
<para>The pointcut <function>goCut</function> is defined as
224
<literal><![CDATA[cflow(this(Demo)) && execution(void
225
go())]]></literal> so that only executions made in the control
226
flow of <literal>Demo.go</literal> are intercepted. The control
227
flow from the method <literal>go</literal> includes the execution of
228
<literal>go</literal> itself, so the definition of the around
229
advice includes <literal>!execution(* go())</literal> to exclude it
230
from the set of executions advised. </para>
234
<title>Printing the class and method name</title>
236
<para>The name of the method and that method's defining class are
237
available as parts of the <ulink
238
url="../api/org/aspectj/lang/Signature.html">Signature</ulink>,
239
found using the method <literal>getSignature</literal> of either
240
<literal>thisJoinPoint</literal> or
241
<literal>thisJoinPointStaticPart</literal>. </para>
243
<programlisting><![CDATA[
246
static final void println(String s){ System.out.println(s); }
248
pointcut goCut(): cflow(this(Demo) && execution(void go()));
250
pointcut demoExecs(): within(Demo) && execution(* *(..));
252
Object around(): demoExecs() && !execution(* go()) && goCut() {
253
println("Intercepted message: " +
254
thisJoinPointStaticPart.getSignature().getName());
255
println("in class: " +
256
thisJoinPointStaticPart.getSignature().getDeclaringType().getName());
257
printParameters(thisJoinPoint);
258
println("Running original method: \n" );
259
Object result = proceed();
260
println(" result: " + result );
264
static private void printParameters(JoinPoint jp) {
265
println("Arguments: " );
266
Object[] args = jp.getArgs();
267
String[] names = ((CodeSignature)jp.getSignature()).getParameterNames();
268
Class[] types = ((CodeSignature)jp.getSignature()).getParameterTypes();
269
for (int i = 0; i < args.length; i++) {
270
println(" " + i + ". " + names[i] +
271
" : " + types[i].getName() +
280
<title>Printing the parameters</title>
283
The static portions of the parameter details, the name and
284
types of the parameters, can be accessed through the <ulink
285
url="../api/org/aspectj/lang/reflect/CodeSignature.html"><literal>CodeSignature</literal></ulink>
286
associated with the join point. All execution join points have code
287
signatures, so the cast to <literal>CodeSignature</literal>
291
The dynamic portions of the parameter details, the actual
292
values of the parameters, are accessed directly from the execution
293
join point object. </para>
298
<sect2 id="sec:RolesAndViews">
299
<title>Roles and Views Using Introduction</title>
300
<titleabbrev id="sec:RolesAndViews:title">Roles and Views Using
301
Introduction</titleabbrev>
303
<para>(The code for this example is in
304
<filename><replaceable>InstallDir</replaceable>/examples/introduction</filename>.)</para>
306
<para>Like advice, pieces of introduction are members of an aspect. They
307
define new members that act as if they were defined on another
308
class. Unlike advice, introduction affects not only the behavior of the
309
application, but also the structural relationship between an
310
application's classes. </para>
312
<para>This is crucial: Affecting the class structure of an application at
313
makes these modifications available to other components of the
316
<para>Introduction modifies a class by adding or changing</para>
317
<itemizedlist spacing="compact">
318
<listitem>member fields</listitem>
319
<listitem>member methods</listitem>
320
<listitem>nested classes</listitem>
323
<para>and by making the class</para>
325
<itemizedlist spacing="compact">
326
<listitem>implement interfaces</listitem>
327
<listitem>extend classes</listitem>
331
This example provides three illustrations of the use of introduction to
332
encapsulate roles or views of a class. The class we will be introducing
333
into, <classname>Point</classname>, is a simple class with rectangular
334
and polar coordinates. Our introduction will make the class
335
<classname>Point</classname>, in turn, cloneable, hashable, and
336
comparable. These facilities are provided by introduction forms without
337
having to modify the class <classname>Point</classname>.
341
<title>The class <classname>Point</classname></title>
343
<para>The class <classname>Point</classname> defines geometric points
344
whose interface includes polar and rectangular coordinates, plus some
345
simple operations to relocate points. <classname>Point</classname>'s
346
implementation has attributes for both its polar and rectangular
347
coordinates, plus flags to indicate which currently reflect the
348
position of the point. Some operations cause the polar coordinates to
349
be updated from the rectangular, and some have the opposite effect.
350
This implementation, which is in intended to give the minimum number
351
of conversions between coordinate systems, has the property that not
352
all the attributes stored in a <classname>Point</classname> object
353
are necessary to give a canonical representation such as might be
354
used for storing, comparing, cloning or making hash codes from
355
points. Thus the aspects, though simple, are not totally trivial.
359
The diagram below gives an overview of the aspects and their
360
interaction with the class <classname>Point</classname>.</para>
365
<imagedata fileref="aspects.gif"/>
374
<title>Making <classname>Point</classname>s Cloneable — The Aspect
375
<classname>CloneablePoint</classname></title>
377
<para>This first example demonstrates the introduction of a interface
378
(<classname>Cloneable</classname>) and a method
379
(<function>clone</function>) into the class
380
<classname>Point</classname>. In Java, all objects inherit the method
381
<literal>clone</literal> from the class
382
<classname>Object</classname>, but an object is not cloneable unless
383
its class also implements the interface
384
<classname>Cloneable</classname>. In addition, classes frequently
385
have requirements over and above the simple bit-for-bit copying that
386
<literal>Object.clone</literal> does. In our case, we want to update
387
a <classname>Point</classname>'s coordinate systems before we
388
actually clone the <classname>Point</classname>. So we have to
389
override <literal>Object.clone</literal> with a new method that does
390
what we want. </para>
392
<para>The <classname>CloneablePoint</classname> aspect uses the
393
<literal>declare parents</literal> form to introduce the interface
394
<classname>Cloneable</classname> into the class
395
<classname>Point</classname>. It then defines a method,
396
<literal>Point.clone</literal>, which overrides the method
397
<function>clone</function> that was inherited from
398
<classname>Object</classname>. <function>Point.clone</function>
399
updates the <classname>Point</classname>'s coordinate systems before
400
invoking its superclass' <function>clone</function> method.</para>
402
<programlisting><![CDATA[
403
public aspect CloneablePoint {
405
declare parents: Point implements Cloneable;
407
public Object Point.clone() throws CloneNotSupportedException {
408
// we choose to bring all fields up to date before cloning.
411
return super.clone();
414
public static void main(String[] args){
415
Point p1 = new Point();
418
p1.setPolar(Math.PI, 1.0);
420
p2 = (Point)p1.clone();
421
} catch (CloneNotSupportedException e) {}
422
System.out.println("p1 =" + p1 );
423
System.out.println("p2 =" + p2 );
425
p1.rotate(Math.PI / -2);
426
System.out.println("p1 =" + p1 );
427
System.out.println("p2 =" + p2 );
432
<para>Note that since aspects define types just as classes define
433
types, we can define a <function>main</function> method that is
434
invocable from the command line to use as a test method.</para>
438
<title>Making <classname>Point</classname>s Comparable — The
439
Aspect <classname>ComparablePoint</classname></title>
441
<para>This second example introduces another interface and
442
method into the class <classname>Point</classname>.</para>
444
<para>The interface <classname>Comparable</classname> defines the
445
single method <literal>compareTo</literal> which can be use to define
446
a natural ordering relation among the objects of a class that
447
implement it. </para>
449
<para>The aspect <classname>ComparablePoint</classname> introduces
450
implements <classname>Comparable</classname> into
451
<classname>Point</classname> along with a
452
<literal>compareTo</literal> method that can be used to compare
453
<classname>Point</classname>s. A <classname>Point</classname>
454
<literal>p1</literal> is said to be less than
455
another <classname>Point</classname><literal> p2</literal> if
456
<literal>p1</literal> is closer to the origin. </para>
458
<programlisting><![CDATA[
459
public aspect ComparablePoint {
461
declare parents: Point implements Comparable;
463
public int Point.compareTo(Object o) {
464
return (int) (this.getRho() - ((Point)o).getRho());
467
public static void main(String[] args){
468
Point p1 = new Point();
469
Point p2 = new Point();
471
System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
473
p1.setRectangular(2,5);
474
p2.setRectangular(2,5);
475
System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
477
p2.setRectangular(3,6);
478
System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
480
p1.setPolar(Math.PI, 4);
481
p2.setPolar(Math.PI, 4);
482
System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
484
p1.rotate(Math.PI / 4.0);
485
System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
488
System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
490
}]]></programlisting>
494
<title>Making <classname>Point</classname>s Hashable — The Aspect
495
<classname>HashablePoint</classname></title>
497
<para>The third aspect overrides two previously defined methods to
498
give to <classname>Point</classname> the hashing behavior we
501
<para>The method <literal>Object.hashCode</literal> returns an unique
502
integer, suitable for use as a hash table key. Different
503
implementations are allowed return different integers, but must
504
return distinct integers for distinct objects, and the same integer
505
for objects that test equal. But since the default implementation
506
of <literal>Object.equal</literal> returns <literal>true</literal>
507
only when two objects are identical, we need to redefine both
508
<function>equals</function> and <function>hashCode</function> to work
509
correctly with objects of type <classname>Point</classname>. For
510
example, we want two <classname>Point</classname> objects to test
511
equal when they have the same <literal>x</literal> and
512
<literal>y</literal> values, or the same <literal>rho</literal> and
513
<literal>theta</literal> values, not just when they refer to the same
514
object. We do this by overriding the methods
515
<literal>equals</literal> and <literal>hashCode</literal> in the
516
class <classname>Point</classname>. </para>
518
<para>The class <classname>HashablePoint</classname> introduces the
519
methods <literal>hashCode</literal> and <literal>equals</literal>
520
into the class <classname>Point</classname>. These methods use
521
<classname>Point</classname>'s rectangular coordinates to generate a
522
hash code and to test for equality. The <literal>x</literal> and
523
<literal>y</literal> coordinates are obtained using the appropriate
524
get methods, which ensure the rectangular coordinates are up-to-date
525
before returning their values. </para>
527
<programlisting><![CDATA[
528
public aspect HashablePoint {
530
public int Point.hashCode() {
531
return (int) (getX() + getY() % Integer.MAX_VALUE);
534
public boolean Point.equals(Object o) {
535
if (o == this) { return true; }
536
if (!(o instanceof Point)) { return false; }
537
Point other = (Point)o;
538
return (getX() == other.getX()) && (getY() == other.getY());
541
public static void main(String[] args) {
542
Hashtable h = new Hashtable();
543
Point p1 = new Point();
545
p1.setRectangular(10, 10);
546
Point p2 = new Point();
548
p2.setRectangular(10, 10);
550
System.out.println("p1 = " + p1);
551
System.out.println("p2 = " + p2);
552
System.out.println("p1.hashCode() = " + p1.hashCode());
553
System.out.println("p2.hashCode() = " + p2.hashCode());
556
System.out.println("Got: " + h.get(p2));
561
<para> Again, we supply a <literal>main</literal> method in the aspect
571
<!-- ============================================================ -->
572
<!-- ============================================================ -->
575
<title>Development Aspects</title>
578
<title>Tracing Aspects</title>
580
<para>(The code for this example is in
581
<filename><replaceable>InstallDir</replaceable>/examples/tracing</filename>.)
585
<title>Overview</title>
588
Writing a class that provides tracing functionality is easy: a couple
589
of functions, a boolean flag for turning tracing on and off, a choice
590
for an output stream, maybe some code for formatting the output---these
591
are all elements that <classname>Trace</classname> classes have been
592
known to have. <classname>Trace</classname> classes may be highly
593
sophisticated, too, if the task of tracing the execution of a program
598
But developing the support for tracing is just one part of the effort
599
of inserting tracing into a program, and, most likely, not the biggest
600
part. The other part of the effort is calling the tracing functions at
601
appropriate times. In large systems, this interaction with the tracing
602
support can be overwhelming. Plus, tracing is one of those things that
603
slows the system down, so these calls should often be pulled out of the
604
system before the product is shipped. For these reasons, it is not
605
unusual for developers to write ad-hoc scripting programs that rewrite
606
the source code by inserting/deleting trace calls before and after the
611
AspectJ can be used for some of these tracing concerns in a less ad-hoc
612
way. Tracing can be seen as a concern that crosscuts the entire system
613
and as such is amenable to encapsulation in an aspect. In addition, it
614
is fairly independent of what the system is doing. Therefore tracing is
615
one of those kind of system aspects that can potentially be plugged in
616
and unplugged without any side-effects in the basic functionality of
622
<title>An Example Application</title>
625
Throughout this example we will use a simple application that contains
626
only four classes. The application is about shapes. The
627
<classname>TwoDShape</classname> class is the root of the shape
631
<programlisting><![CDATA[
632
public abstract class TwoDShape {
633
protected double x, y;
634
protected TwoDShape(double x, double y) {
635
this.x = x; this.y = y;
637
public double getX() { return x; }
638
public double getY() { return y; }
639
public double distance(TwoDShape s) {
640
double dx = Math.abs(s.getX() - x);
641
double dy = Math.abs(s.getY() - y);
642
return Math.sqrt(dx*dx + dy*dy);
644
public abstract double perimeter();
645
public abstract double area();
646
public String toString() {
647
return (" @ (" + String.valueOf(x) + ", " + String.valueOf(y) + ") ");
653
<classname>TwoDShape</classname> has two subclasses,
654
<classname>Circle</classname> and <classname>Square</classname>:
657
<programlisting><![CDATA[
658
public class Circle extends TwoDShape {
660
public Circle(double x, double y, double r) {
661
super(x, y); this.r = r;
663
public Circle(double x, double y) { this( x, y, 1.0); }
664
public Circle(double r) { this(0.0, 0.0, r); }
665
public Circle() { this(0.0, 0.0, 1.0); }
666
public double perimeter() {
667
return 2 * Math.PI * r;
669
public double area() {
670
return Math.PI * r*r;
672
public String toString() {
673
return ("Circle radius = " + String.valueOf(r) + super.toString());
678
<programlisting><![CDATA[
679
public class Square extends TwoDShape {
680
protected double s; // side
681
public Square(double x, double y, double s) {
682
super(x, y); this.s = s;
684
public Square(double x, double y) { this( x, y, 1.0); }
685
public Square(double s) { this(0.0, 0.0, s); }
686
public Square() { this(0.0, 0.0, 1.0); }
687
public double perimeter() {
690
public double area() {
693
public String toString() {
694
return ("Square side = " + String.valueOf(s) + super.toString());
700
To run this application, compile the classes. You can do it with or
701
without ajc, the AspectJ compiler. If you've installed AspectJ, go to
703
<filename><replaceable>InstallDir</replaceable>/examples</filename> and
708
ajc -argfile tracing/notrace.lst
711
<para>To run the program, type</para>
714
java tracing.ExampleMain
717
<para>(we don't need anything special on the classpath since this is pure
718
Java code). You should see the following output:</para>
720
<programlisting><![CDATA[
721
c1.perimeter() = 12.566370614359172
722
c1.area() = 12.566370614359172
725
c2.distance(c1) = 4.242640687119285
726
s1.distance(c1) = 2.23606797749979
727
s1.toString(): Square side = 1.0 @ (1.0, 2.0)
732
<title>Tracing—Version 1</title>
735
In a first attempt to insert tracing in this application, we will start
736
by writing a <classname>Trace</classname> class that is exactly what we
737
would write if we didn't have aspects. The implementation is in
738
<filename>version1/Trace.java</filename>. Its public interface is:
741
<programlisting><![CDATA[
743
public static int TRACELEVEL = 0;
744
public static void initStream(PrintStream s) {...}
745
public static void traceEntry(String str) {...}
746
public static void traceExit(String str) {...}
751
If we didn't have AspectJ, we would have to insert calls to
752
<literal>traceEntry</literal> and <literal>traceExit</literal> in all
753
methods and constructors we wanted to trace, and to initialize
754
<literal>TRACELEVEL</literal> and the stream. If we wanted to trace all
755
the methods and constructors in our example, that would amount to
756
around 40 calls, and we would hope we had not forgotten any method. But
757
we can do that more consistently and reliably with the following
758
aspect (found in <filename>version1/TraceMyClasses.java</filename>):
761
<programlisting><![CDATA[
762
aspect TraceMyClasses {
763
pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);
764
pointcut myConstructor(): myClass() && execution(new(..));
765
pointcut myMethod(): myClass() && execution(* *(..));
767
before (): myConstructor() {
768
Trace.traceEntry("" + thisJoinPointStaticPart.getSignature());
770
after(): myConstructor() {
771
Trace.traceExit("" + thisJoinPointStaticPart.getSignature());
774
before (): myMethod() {
775
Trace.traceEntry("" + thisJoinPointStaticPart.getSignature());
777
after(): myMethod() {
778
Trace.traceExit("" + thisJoinPointStaticPart.getSignature());
780
}]]></programlisting>
783
This aspect performs the tracing calls at appropriate times. According
784
to this aspect, tracing is performed at the entrance and exit of every
785
method and constructor defined within the shape hierarchy.
789
What is printed at before and after each of the traced
790
join points is the signature of the method executing. Since the
791
signature is static information, we can get it through
792
<literal>thisJoinPointStaticPart</literal>.
796
To run this version of tracing, go to the directory
797
<filename><replaceable>InstallDir</replaceable>/examples</filename> and
801
<programlisting><![CDATA[
802
ajc -argfile tracing/tracev1.lst
806
Running the main method of
807
<classname>tracing.version1.TraceMyClasses</classname> should produce
811
<programlisting><![CDATA[
812
--> tracing.TwoDShape(double, double)
813
<-- tracing.TwoDShape(double, double)
814
--> tracing.Circle(double, double, double)
815
<-- tracing.Circle(double, double, double)
816
--> tracing.TwoDShape(double, double)
817
<-- tracing.TwoDShape(double, double)
818
--> tracing.Circle(double, double, double)
819
<-- tracing.Circle(double, double, double)
820
--> tracing.Circle(double)
821
<-- tracing.Circle(double)
822
--> tracing.TwoDShape(double, double)
823
<-- tracing.TwoDShape(double, double)
824
--> tracing.Square(double, double, double)
825
<-- tracing.Square(double, double, double)
826
--> tracing.Square(double, double)
827
<-- tracing.Square(double, double)
828
--> double tracing.Circle.perimeter()
829
<-- double tracing.Circle.perimeter()
830
c1.perimeter() = 12.566370614359172
831
--> double tracing.Circle.area()
832
<-- double tracing.Circle.area()
833
c1.area() = 12.566370614359172
834
--> double tracing.Square.perimeter()
835
<-- double tracing.Square.perimeter()
837
--> double tracing.Square.area()
838
<-- double tracing.Square.area()
840
--> double tracing.TwoDShape.distance(TwoDShape)
841
--> double tracing.TwoDShape.getX()
842
<-- double tracing.TwoDShape.getX()
843
--> double tracing.TwoDShape.getY()
844
<-- double tracing.TwoDShape.getY()
845
<-- double tracing.TwoDShape.distance(TwoDShape)
846
c2.distance(c1) = 4.242640687119285
847
--> double tracing.TwoDShape.distance(TwoDShape)
848
--> double tracing.TwoDShape.getX()
849
<-- double tracing.TwoDShape.getX()
850
--> double tracing.TwoDShape.getY()
851
<-- double tracing.TwoDShape.getY()
852
<-- double tracing.TwoDShape.distance(TwoDShape)
853
s1.distance(c1) = 2.23606797749979
854
--> String tracing.Square.toString()
855
--> String tracing.TwoDShape.toString()
856
<-- String tracing.TwoDShape.toString()
857
<-- String tracing.Square.toString()
858
s1.toString(): Square side = 1.0 @ (1.0, 2.0)
862
When <filename>TraceMyClasses.java</filename> is not provided to
863
<command>ajc</command>, the aspect does not have any affect on the
864
system and the tracing is unplugged.
869
<title>Tracing—Version 2</title>
872
Another way to accomplish the same thing would be to write a reusable
873
tracing aspect that can be used not only for these application classes,
874
but for any class. One way to do this is to merge the tracing
875
functionality of <literal>Trace—version1</literal> with the
876
crosscutting support of
877
<literal>TraceMyClasses—version1</literal>. We end up with a
878
<literal>Trace</literal> aspect (found in
879
<filename>version2/Trace.java</filename>) with the following public
883
<programlisting><![CDATA[
884
abstract aspect Trace {
886
public static int TRACELEVEL = 2;
887
public static void initStream(PrintStream s) {...}
888
protected static void traceEntry(String str) {...}
889
protected static void traceExit(String str) {...}
890
abstract pointcut myClass();
895
In order to use it, we need to define our own subclass that knows about
896
our application classes, in <filename>version2/TraceMyClasses.java</filename>:
899
<programlisting><![CDATA[
900
public aspect TraceMyClasses extends Trace {
901
pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);
903
public static void main(String[] args) {
904
Trace.TRACELEVEL = 2;
905
Trace.initStream(System.err);
906
ExampleMain.main(args);
912
Notice that we've simply made the pointcut <literal>classes</literal>,
913
that was an abstract pointcut in the super-aspect, concrete. To run
914
this version of tracing, go to the directory
915
<filename>examples</filename> and type:
918
<programlisting><![CDATA[
919
ajc -argfile tracing/tracev2.lst
923
The file tracev2.lst lists the application classes as well as this
924
version of the files Trace.java and TraceMyClasses.java. Running the
925
main method of <classname>tracing.version2.TraceMyClasses</classname>
926
should output exactly the same trace information as that from version
931
The entire implementation of the new <classname>Trace</classname> class
935
<programlisting><![CDATA[
936
abstract aspect Trace {
938
// implementation part
940
public static int TRACELEVEL = 2;
941
protected static PrintStream stream = System.err;
942
protected static int callDepth = 0;
944
public static void initStream(PrintStream s) {
947
protected static void traceEntry(String str) {
948
if (TRACELEVEL == 0) return;
949
if (TRACELEVEL == 2) callDepth++;
952
protected static void traceExit(String str) {
953
if (TRACELEVEL == 0) return;
955
if (TRACELEVEL == 2) callDepth--;
957
private static void printEntering(String str) {
959
stream.println("--> " + str);
961
private static void printExiting(String str) {
963
stream.println("<-- " + str);
965
private static void printIndent() {
966
for (int i = 0; i < callDepth; i++)
972
abstract pointcut myClass();
974
pointcut myConstructor(): myClass() && execution(new(..));
975
pointcut myMethod(): myClass() && execution(* *(..));
977
before(): myConstructor() {
978
traceEntry("" + thisJoinPointStaticPart.getSignature());
980
after(): myConstructor() {
981
traceExit("" + thisJoinPointStaticPart.getSignature());
984
before(): myMethod() {
985
traceEntry("" + thisJoinPointStaticPart.getSignature());
987
after(): myMethod() {
988
traceExit("" + thisJoinPointStaticPart.getSignature());
994
This version differs from version 1 in several subtle ways. The first
995
thing to notice is that this <classname>Trace</classname> class merges
996
the functional part of tracing with the crosscutting of the tracing
997
calls. That is, in version 1, there was a sharp separation between the
998
tracing support (the class <classname>Trace</classname>) and the
999
crosscutting usage of it (by the class
1000
<classname>TraceMyClasses</classname>). In this version those two
1001
things are merged. That's why the description of this class explicitly
1002
says that "Trace messages are printed before and after constructors and
1003
methods are," which is what we wanted in the first place. That is, the
1004
placement of the calls, in this version, is established by the aspect
1005
class itself, leaving less opportunity for misplacing calls.</para>
1008
A consequence of this is that there is no need for providing traceEntry
1009
and traceExit as public operations of this class. You can see that they
1010
were classified as protected. They are supposed to be internal
1011
implementation details of the advice.
1015
The key piece of this aspect is the abstract pointcut classes that
1016
serves as the base for the definition of the pointcuts constructors and
1017
methods. Even though <classname>classes</classname> is abstract, and
1018
therefore no concrete classes are mentioned, we can put advice on it,
1019
as well as on the pointcuts that are based on it. The idea is "we don't
1020
know exactly what the pointcut will be, but when we do, here's what we
1021
want to do with it." In some ways, abstract pointcuts are similar to
1022
abstract methods. Abstract methods don't provide the implementation,
1023
but you know that the concrete subclasses will, so you can invoke those
1030
<!-- ============================================================ -->
1031
<!-- ============================================================ -->
1034
<title>Production Aspects</title>
1036
<!-- ==================== -->
1038
<sect2><!-- A Bean Aspect -->
1039
<title>A Bean Aspect</title>
1041
<para>(The code for this example is in
1042
<filename><replaceable>InstallDir</replaceable>/examples/bean</filename>.)
1046
This example examines an aspect that makes Point objects into a Java beans
1047
with bound properties. </para>
1050
<title>Introduction</title>
1052
Java beans are reusable software components that can be visually
1053
manipulated in a builder tool. The requirements for an object to be a
1054
bean are few. Beans must define a no-argument constructor and must be
1055
either <classname>Serializable</classname> or
1056
<classname>Externalizable</classname>. Any properties of the object
1057
that are to be treated as bean properties should be indicated by the
1058
presence of appropriate <literal>get</literal> and
1059
<literal>set</literal> methods whose names are
1060
<literal>get</literal><emphasis>property</emphasis> and
1061
<literal>set </literal><emphasis>property</emphasis>
1062
where <emphasis>property</emphasis> is the name of a field in the bean
1063
class. Some bean properties, known as bound properties, fire events
1064
whenever their values change so that any registered listeners (such as,
1065
other beans) will be informed of those changes. Making a bound property
1066
involves keeping a list of registered listeners, and creating and
1067
dispatching event objects in methods that change the property values,
1068
such as set<emphasis>property</emphasis> methods.</para>
1071
<classname>Point</classname> is a simple class representing points with
1072
rectangular coordinates. <classname>Point</classname> does not know
1073
anything about being a bean: there are set methods for
1074
<literal>x</literal> and <literal>y</literal> but they do not fire
1075
events, and the class is not serializable. Bound is an aspect that
1076
makes <classname>Point</classname> a serializable class and makes its
1077
<literal>get</literal> and <literal>set</literal> methods support the
1078
bound property protocol.
1083
<title>The Class <classname>Point</classname></title>
1086
The class <classname>Point</classname> is a very simple class with
1087
trivial getters and setters, and a simple vector offset method.
1090
<programlisting><![CDATA[
1093
protected int x = 0;
1094
protected int y = 0;
1104
public void setRectangular(int newX, int newY) {
1109
public void setX(int newX) {
1113
public void setY(int newY) {
1117
public void offset(int deltaX, int deltaY) {
1118
setRectangular(x + deltaX, y + deltaY);
1121
public String toString() {
1122
return "(" + getX() + ", " + getY() + ")" ;
1124
}]]></programlisting>
1129
<title>The Aspect <classname>BoundPoint</classname></title>
1132
The aspect <classname>BoundPoint</classname> adds "beanness" to
1133
<classname>Point</classname> objects. The first thing it does is
1134
privately introduce a reference to an instance of
1135
<classname>PropertyChangeSupport</classname> into all
1136
<classname>Point</classname> objects. The property change
1137
support object must be constructed with a reference to the bean for
1138
which it is providing support, so it is initialized by passing it this,
1139
an instance of <classname>Point</classname>. The support field is
1140
privately introduced, so only the code in the aspect can refer to it.
1144
Methods for registering and managing listeners for property change
1145
events are introduced into <classname>Point</classname> by the
1146
introductions. These methods delegate the work to the
1147
property change support object.
1151
The introduction also makes <classname>Point</classname> implement the
1152
<classname>Serializable</classname> interface. Implementing
1153
<classname>Serializable</classname> does not require any methods to be
1154
implemented. Serialization for <classname>Point</classname> objects is
1155
provided by the default serialization method.
1159
The pointcut <function>setters</function> names the
1160
<literal>set</literal> methods: reception by a
1161
<classname>Point</classname> object of any method whose name begins
1162
with '<literal>set</literal>' and takes one parameter. The around
1163
advice on <literal>setters()</literal> stores the values
1164
of the <literal>X</literal> and <literal>Y</literal> properties, calls
1165
the original <literal>set</literal> method and then fires the
1166
appropriate property change event according to which set method was
1167
called. Note that the call to the method proceed needs to pass along
1168
the <literal>Point p</literal>. The rule of thumb is that context that
1169
an around advice exposes must be passed forward to continue.
1172
<programlisting><![CDATA[
1174
private PropertyChangeSupport Point.support = new PropertyChangeSupport(this);
1176
public void Point.addPropertyChangeListener(PropertyChangeListener listener){
1177
support.addPropertyChangeListener(listener);
1180
public void Point.addPropertyChangeListener(String propertyName,
1181
PropertyChangeListener listener){
1183
support.addPropertyChangeListener(propertyName, listener);
1186
public void Point.removePropertyChangeListener(String propertyName,
1187
PropertyChangeListener listener) {
1188
support.removePropertyChangeListener(propertyName, listener);
1191
public void Point.removePropertyChangeListener(PropertyChangeListener listener) {
1192
support.removePropertyChangeListener(listener);
1195
public void Point.hasListeners(String propertyName) {
1196
support.hasListeners(propertyName);
1199
declare parents: Point implements Serializable;
1201
pointcut setter(Point p): call(void Point.set*(*)) && target(p);
1203
void around(Point p): setter(p) {
1204
String propertyName =
1205
thisJoinPointStaticPart.getSignature().getName().substring("set".length());
1206
int oldX = p.getX();
1207
int oldY = p.getY();
1209
if (propertyName.equals("X")){
1210
firePropertyChange(p, propertyName, oldX, p.getX());
1212
firePropertyChange(p, propertyName, oldY, p.getY());
1216
void firePropertyChange(Point p,
1220
p.support.firePropertyChange(property,
1222
new Double(newval));
1225
]]></programlisting>
1230
<title>The Test Program</title>
1233
The test program registers itself as a property change listener to a
1234
<literal>Point</literal> object that it creates and then performs
1235
simple manipulation of that point: calling its set methods and the
1236
offset method. Then it serializes the point and writes it to a file and
1237
then reads it back. The result of saving and restoring the point is that
1238
a new point is created.
1241
<programlisting><![CDATA[
1242
class Demo implements PropertyChangeListener {
1244
static final String fileName = "test.tmp";
1246
public void propertyChange(PropertyChangeEvent e){
1247
System.out.println("Property " + e.getPropertyName() + " changed from " +
1248
e.getOldValue() + " to " + e.getNewValue() );
1251
public static void main(String[] args){
1252
Point p1 = new Point();
1253
p1.addPropertyChangeListener(new Demo());
1254
System.out.println("p1 =" + p1);
1255
p1.setRectangular(5,2);
1256
System.out.println("p1 =" + p1);
1259
System.out.println("p1 =" + p1);
1261
System.out.println("p1 =" + p1);
1263
Point p2 = (Point) restore(fileName);
1264
System.out.println("Had: " + p1);
1265
System.out.println("Got: " + p2);
1269
]]></programlisting>
1273
<title>Compiling and Running the Example</title>
1274
<para>To compile and run this example, go to the examples directory and type:
1277
<programlisting><![CDATA[
1278
ajc -argfile bean/files.lst
1280
]]></programlisting>
1285
<!-- ==================== -->
1287
<sect2><!-- The Subject/Observer Protocol -->
1288
<title>The Subject/Observer Protocol</title>
1290
<para>(The code for this example is in
1291
<filename><replaceable>InstallDir</replaceable>/examples/observer</filename>.)
1295
This demo illustrates how the Subject/Observer design pattern can be
1296
coded with aspects. </para>
1299
<title>Overview</title>
1301
The demo consists of the following: A colored label is a renderable
1302
object that has a color that cycles through a set of colors, and a
1303
number that records the number of cycles it has been through. A button
1304
is an action item that records when it is clicked.
1308
With these two kinds of objects, we can build up a Subject/Observer
1309
relationship in which colored labels observe the clicks of buttons;
1310
that is, where colored labels are the observers and buttons are the
1315
The demo is designed and implemented using the Subject/Observer design
1316
pattern. The remainder of this example explains the classes and aspects
1317
of this demo, and tells you how to run it.
1322
<title>Generic Components</title>
1325
The generic parts of the protocol are the interfaces
1326
<classname>Subject</classname> and <classname>Observer</classname>, and
1327
the abstract aspect <classname>SubjectObserverProtocol</classname>. The
1328
<classname>Subject</classname> interface is simple, containing methods
1329
to add, remove, and view <classname>Observer</classname> objects, and a
1330
method for getting data about state changes:
1333
<programlisting><![CDATA[
1335
void addObserver(Observer obs);
1336
void removeObserver(Observer obs);
1337
Vector getObservers();
1340
]]></programlisting>
1342
<para> The <classname>Observer</classname> interface is just as simple,
1343
with methods to set and get <classname>Subject</classname> objects, and
1344
a method to call when the subject gets updated.
1347
<programlisting><![CDATA[
1348
interface Observer {
1349
void setSubject(Subject s);
1350
Subject getSubject();
1353
]]></programlisting>
1356
The <classname>SubjectObserverProtocol</classname> aspect contains
1357
within it all of the generic parts of the protocol, namely, how to fire
1358
the <classname>Observer</classname> objects' update methods when some
1359
state changes in a subject.
1362
<programlisting><![CDATA[
1363
abstract aspect SubjectObserverProtocol {
1365
abstract pointcut stateChanges(Subject s);
1367
after(Subject s): stateChanges(s) {
1368
for (int i = 0; i < s.getObservers().size(); i++) {
1369
((Observer)s.getObservers().elementAt(i)).update();
1373
private Vector Subject.observers = new Vector();
1374
public void Subject.addObserver(Observer obs) {
1375
observers.addElement(obs);
1376
obs.setSubject(this);
1378
public void Subject.removeObserver(Observer obs) {
1379
observers.removeElement(obs);
1380
obs.setSubject(null);
1382
public Vector Subject.getObservers() { return observers; }
1384
private Subject Observer.subject = null;
1385
public void Observer.setSubject(Subject s) { subject = s; }
1386
public Subject Observer.getSubject() { return subject; }
1389
]]></programlisting>
1392
Note that this aspect does three things. It define an abstract pointcut
1393
that extending aspects can override. It defines advice that should run
1394
after the join points of the pointcut. And it introduces state and
1395
behavior onto the <classname>Subject</classname> and
1396
<classname>Observer</classname> interfaces.
1401
<title>Application Classes</title>
1403
<para> <classname>Button</classname> objects extend
1404
<classname>java.awt.Button</classname>, and all they do is make sure
1405
the <literal>void click()</literal> method is called whenever a button
1409
<programlisting><![CDATA[
1410
class Button extends java.awt.Button {
1412
static final Color defaultBackgroundColor = Color.gray;
1413
static final Color defaultForegroundColor = Color.black;
1414
static final String defaultText = "cycle color";
1416
Button(Display display) {
1418
setLabel(defaultText);
1419
setBackground(defaultBackgroundColor);
1420
setForeground(defaultForegroundColor);
1421
addActionListener(new ActionListener() {
1422
public void actionPerformed(ActionEvent e) {
1423
Button.this.click();
1426
display.addToFrame(this);
1429
public void click() {}
1432
]]></programlisting>
1435
Note that this class knows nothing about being a Subject.
1438
ColorLabel objects are labels that support the void colorCycle()
1439
method. Again, they know nothing about being an observer.
1442
<programlisting><![CDATA[
1443
class ColorLabel extends Label {
1445
ColorLabel(Display display) {
1447
display.addToFrame(this);
1450
final static Color[] colors = {Color.red, Color.blue,
1451
Color.green, Color.magenta};
1452
private int colorIndex = 0;
1453
private int cycleCount = 0;
1456
colorIndex = (colorIndex + 1) % colors.length;
1457
setBackground(colors[colorIndex]);
1458
setText("" + cycleCount);
1461
]]></programlisting>
1464
Finally, the <classname>SubjectObserverProtocolImpl</classname>
1465
implements the subject/observer protocol, with
1466
<classname>Button</classname> objects as subjects and
1467
<classname>ColorLabel</classname> objects as observers:
1470
<programlisting><![CDATA[
1473
import java.util.Vector;
1475
aspect SubjectObserverProtocolImpl extends SubjectObserverProtocol {
1477
declare parents: Button implements Subject;
1478
public Object Button.getData() { return this; }
1480
declare parents: ColorLabel implements Observer;
1481
public void ColorLabel.update() {
1485
pointcut stateChanges(Subject s):
1487
call(void Button.click());
1489
}]]></programlisting>
1492
It does this by introducing the appropriate interfaces onto the
1493
<classname>Button</classname> and <classname>ColorLabel</classname>
1494
classes, making sure the methods required by the interfaces are
1495
implemented, and providing a definition for the
1496
<literal>stateChanges</literal> pointcut. Now, every time a
1497
<classname>Button</classname> is clicked, all
1498
<classname>ColorLabel</classname> objects observing that button will
1499
<literal>colorCycle</literal>.
1504
<title>Compiling and Running</title>
1506
<para> <classname>Demo</classname> is the top class that starts this
1507
demo. It instantiates a two buttons and three observers and links them
1508
together as subjects and observers. So to run the demo, go to the
1509
<filename>examples</filename> directory and type:
1512
<programlisting><![CDATA[
1513
ajc -argfile observer/files.lst
1515
]]></programlisting>
1520
<!-- ==================== -->
1523
<title>A Simple Telecom Simulation</title>
1525
<para>(The code for this example is in
1526
<filename><replaceable>InstallDir</replaceable>/examples/telecom</filename>.)
1530
This example illustrates some ways that dependent concerns can be encoded
1531
with aspects. It uses an example system comprising a simple model of
1532
telephone connections to which timing and billing features are added
1533
using aspects, where the billing feature depends upon the timing feature.
1537
<title>The Application</title>
1540
The example application is a simple simulation of a telephony system in
1541
which customers make, accept, merge and hang-up both local and long
1542
distance calls. The application architecture is in three layers.
1547
The basic objects provide basic functionality to simulate
1548
customers, calls and connections (regular calls have one
1549
connection, conference calls have more than one).
1555
The timing feature is concerned with timing the connections and
1556
keeping the total connection time per customer. Aspects are used to
1557
add a timer to each connection and to manage the total time per
1564
The billing feature is concerned with charging customers for the
1565
calls they make. Aspects are used to calculate a charge per
1566
connection and, upon termination of a connection, to add the charge
1567
to the appropriate customer's bill. The billing aspect builds upon
1568
the timing aspect: it uses a pointcut defined in Timing and it uses
1569
the timers that are associated with connections.
1574
The simulation of system has three configurations: basic, timing and
1575
billing. Programs for the three configurations are in classes
1576
<classname>BasicSimulation</classname>,
1577
<classname>TimingSimulation</classname> and
1578
<classname>BillingSimulation</classname>. These share a common
1579
superclass <classname>AbstractSimulation</classname>, which defines the
1580
method run with the simulation itself and the method wait used to
1581
simulate elapsed time.
1586
<title>The Basic Objects</title>
1589
The telecom simulation comprises the classes
1590
<classname>Customer</classname>, <classname>Call</classname> and the
1591
abstract class <classname>Connection</classname> with its two concrete
1592
subclasses <classname>Local</classname> and
1593
<classname>LongDistance</classname>. Customers have a name and a
1594
numeric area code. They also have methods for managing calls. Simple
1595
calls are made between one customer (the caller) and another (the
1596
receiver), a <classname>Connection</classname> object is used to
1597
connect them. Conference calls between more than two customers will
1598
involve more than one connection. A customer may be involved in many
1602
<imagedata fileref="telecom.gif"/>
1604
</inlinemediaobject>
1609
<title>The Class <classname>Customer</classname></title>
1612
<classname>Customer</classname> has methods <literal>call</literal>,
1613
<literal>pickup</literal>, <literal>hangup</literal> and
1614
<literal>merge</literal> for managing calls.
1617
<programlisting><![CDATA[
1618
public class Customer {
1620
private String name;
1621
private int areacode;
1622
private Vector calls = new Vector();
1624
protected void removeCall(Call c){
1625
calls.removeElement(c);
1628
protected void addCall(Call c){
1629
calls.addElement(c);
1632
public Customer(String name, int areacode) {
1634
this.areacode = areacode;
1637
public String toString() {
1638
return name + "(" + areacode + ")";
1641
public int getAreacode(){
1645
public boolean localTo(Customer other){
1646
return areacode == other.areacode;
1649
public Call call(Customer receiver) {
1650
Call call = new Call(this, receiver);
1655
public void pickup(Call call) {
1660
public void hangup(Call call) {
1665
public void merge(Call call1, Call call2){
1670
]]></programlisting>
1675
<title>The Class <classname>Call</classname></title>
1678
Calls are created with a caller and receiver who are customers. If the
1679
caller and receiver have the same area code then the call can be
1680
established with a <classname>Local</classname> connection (see below),
1681
otherwise a <classname>LongDistance</classname> connection is required.
1682
A call comprises a number of connections between customers. Initially
1683
there is only the connection between the caller and receiver but
1684
additional connections can be added if calls are merged to form
1690
<title>The Class <classname>Connection</classname></title>
1692
<para>The class <classname>Connection</classname> models the physical
1693
details of establishing a connection between customers. It does this
1694
with a simple state machine (connections are initially
1695
<literal>PENDING</literal>, then <literal>COMPLETED</literal> and
1696
finally <literal>DROPPED</literal>). Messages are printed to the
1697
console so that the state of connections can be observed. Connection is
1698
an abstract class with two concrete subclasses:
1699
<classname>Local</classname> and <classname>LongDistance</classname>.
1702
<programlisting><![CDATA[
1703
abstract class Connection {
1705
public static final int PENDING = 0;
1706
public static final int COMPLETE = 1;
1707
public static final int DROPPED = 2;
1709
Customer caller, receiver;
1710
private int state = PENDING;
1712
Connection(Customer a, Customer b) {
1717
public int getState(){
1721
public Customer getCaller() { return caller; }
1723
public Customer getReceiver() { return receiver; }
1727
System.out.println("connection completed");
1732
System.out.println("connection dropped");
1735
public boolean connects(Customer c){
1736
return (caller == c || receiver == c);
1740
]]></programlisting>
1745
<title>The Class Local</title>
1747
<programlisting><![CDATA[
1748
class Local extends Connection {
1749
Local(Customer a, Customer b) {
1751
System.out.println("[new local connection from " +
1752
a + " to " + b + "]");
1755
]]></programlisting>
1760
<title>The Class LongDistance</title>
1762
<programlisting><![CDATA[
1763
class LongDistance extends Connection {
1764
LongDistance(Customer a, Customer b) {
1766
System.out.println("[new long distance connection from " +
1767
a + " to " + b + "]");
1770
]]></programlisting>
1775
<title>Compiling and Running the Basic Simulation</title>
1778
The source files for the basic system are listed in the file
1779
<filename>basic.lst</filename>. To build and run the basic system, in a
1780
shell window, type these commands:
1783
<programlisting><![CDATA[
1784
ajc -argfile telecom/basic.lst
1785
java telecom.BasicSimulation
1786
]]></programlisting>
1791
<title>Timing</title>
1793
The <classname>Timing</classname> aspect keeps track of total
1794
connection time for each <classname>Customer</classname> by starting
1795
and stopping a timer associated with each connection. It uses some
1800
<title>The Class <classname>Timer</classname></title>
1803
A <classname>Timer</classname> object simply records the current time
1804
when it is started and stopped, and returns their difference when
1805
asked for the elapsed time. The aspect
1806
<classname>TimerLog</classname> (below) can be used to cause the
1807
start and stop times to be printed to standard output.
1810
<programlisting><![CDATA[
1812
long startTime, stopTime;
1814
public void start() {
1815
startTime = System.currentTimeMillis();
1816
stopTime = startTime;
1819
public void stop() {
1820
stopTime = System.currentTimeMillis();
1823
public long getTime() {
1824
return stopTime - startTime;
1827
]]></programlisting>
1833
<title>The Aspect <classname>TimerLog</classname></title>
1836
The aspect <classname>TimerLog</classname> can be included in a
1837
build to get the timer to announce when it is started and stopped.
1840
<programlisting><![CDATA[
1841
public aspect TimerLog {
1843
after(Timer t): target(t) && call(* Timer.start()) {
1844
System.err.println("Timer started: " + t.startTime);
1847
after(Timer t): target(t) && call(* Timer.stop()) {
1848
System.err.println("Timer stopped: " + t.stopTime);
1851
]]></programlisting>
1856
<title>The Aspect <classname>Timing</classname></title>
1859
The aspect <classname>Timing</classname> introduces attribute
1860
<literal>totalConnectTime</literal> into the class
1861
<classname>Customer</classname> to store the accumulated connection
1862
time per <classname>Customer</classname>. It introduces attribute
1863
timer into <classname>Connection</classname> to associate a timer
1864
with each <classname>Connection</classname>. Two pieces of after
1865
advice ensure that the timer is started when a connection is
1866
completed and and stopped when it is dropped. The pointcut
1867
<literal>endTiming</literal> is defined so that it can be used by the
1868
<classname>Billing</classname> aspect.
1871
<programlisting><![CDATA[
1872
public aspect Timing {
1874
public long Customer.totalConnectTime = 0;
1876
public long getTotalConnectTime(Customer cust) {
1877
return cust.totalConnectTime;
1879
private Timer Connection.timer = new Timer();
1880
public Timer getTimer(Connection conn) { return conn.timer; }
1882
after (Connection c): target(c) && call(void Connection.complete()) {
1883
getTimer(c).start();
1886
pointcut endTiming(Connection c): target(c) &&
1887
call(void Connection.drop());
1889
after(Connection c): endTiming(c) {
1891
c.getCaller().totalConnectTime += getTimer(c).getTime();
1892
c.getReceiver().totalConnectTime += getTimer(c).getTime();
1894
}]]></programlisting>
1899
<title>Billing</title>
1902
The Billing system adds billing functionality to the telecom
1903
application on top of timing.
1907
<title>The Aspect <classname>Billing</classname></title>
1910
The aspect <classname>Billing</classname> introduces attribute
1911
<literal>payer</literal> into <classname>Connection</classname>
1912
to indicate who initiated the call and therefore who is
1913
responsible to pay for it. It also introduces method
1914
<literal>callRate</literal> into <classname>Connection</classname>
1915
so that local and long distance calls can be charged
1916
differently. The call charge must be calculated after the timer is
1917
stopped; the after advice on pointcut
1918
<literal>Timing.endTiming</literal> does this and
1919
<classname>Billing</classname> dominates Timing to make
1920
sure that this advice runs after <classname>Timing's</classname>
1921
advice on the same join point. It introduces attribute
1922
<literal>totalCharge</literal> and its associated methods into
1923
<classname>Customer</classname> (to manage the
1924
customer's bill information.
1927
<programlisting><![CDATA[
1928
public aspect Billing dominates Timing {
1929
// domination required to get advice on endtiming in the right order
1931
public static final long LOCAL_RATE = 3;
1932
public static final long LONG_DISTANCE_RATE = 10;
1935
public Customer Connection.payer;
1936
public Customer getPayer(Connection conn) { return conn.payer; }
1938
after(Customer cust) returning (Connection conn):
1939
args(cust, ..) && call(Connection+.new(..)) {
1943
public abstract long Connection.callRate();
1946
public long LongDistance.callRate() { return LONG_DISTANCE_RATE; }
1947
public long Local.callRate() { return LOCAL_RATE; }
1950
after(Connection conn): Timing.endTiming(conn) {
1951
long time = Timing.aspectOf().getTimer(conn).getTime();
1952
long rate = conn.callRate();
1953
long cost = rate * time;
1954
getPayer(conn).addCharge(cost);
1958
public long Customer.totalCharge = 0;
1959
public long getTotalCharge(Customer cust) { return cust.totalCharge; }
1961
public void Customer.addCharge(long charge){
1962
totalCharge += charge;
1965
]]></programlisting>
1971
<title>Accessing the Introduced State</title>
1974
Both the aspects <classname>Timing</classname> and
1975
<classname>Billing</classname> contain the definition of operations
1976
that the rest of the system may want to access. For example, when
1977
running the simulation with one or both aspects, we want to find out
1978
how much time each customer spent on the telephone and how big their
1979
bill is. That information is also stored in the classes, but they are
1980
accessed through static methods of the aspects, since the state they
1981
refer to is private to the aspect.
1985
Take a look at the file <filename>TimingSimulation.java</filename>. The
1986
most important method of this class is the method
1987
<filename>report(Customer c)</filename>, which is used in the method
1988
run of the superclass <classname>AbstractSimulation</classname>. This
1989
method is intended to print out the status of the customer, with
1990
respect to the <classname>Timing</classname> feature.
1993
<programlisting><![CDATA[
1994
protected void report(Customer c){
1995
Timing t = Timing.aspectOf();
1996
System.out.println(c + " spent " + t.getTotalConnectTime(c));
1998
]]></programlisting>
2003
<title>Compiling and Running</title>
2006
The files timing.lst and billing.lst contain file lists for the timing
2007
and billing configurations. To build and run the application with only
2008
the timing feature, go to the directory examples and type:
2011
<programlisting><![CDATA[
2012
ajc -argfile telecom/timing.lst
2013
java telecom.TimingSimulation
2014
]]></programlisting>
2017
To build and run the application with the timing and billing features,
2018
go to the directory examples and type:
2021
<programlisting><![CDATA[
2022
ajc -argfile telecom/billing.lst
2023
java telecom.BillingSimulation
2024
]]></programlisting>
2029
<title>Discussion</title>
2032
There are some explicit dependencies between the aspects Billing and
2037
Billing is declared to dominate Timing so that Billing's after
2038
advice runs after that of Timing when they are on the same join
2045
Billing uses the pointcut Timing.endTiming.
2051
Billing needs access to the timer associated with a connection.
2060
<!-- ============================================================ -->
2061
<!-- ============================================================ -->
2064
<title>Reusable Aspects</title>
2067
<title>Tracing Aspects Revisited</title>
2069
<para>(The code for this example is in
2070
<filename><replaceable>InstallDir</replaceable>/examples/tracing</filename>.)
2074
<title>Tracing—Version 3</title>
2077
One advantage of not exposing the methods traceEntry and traceExit as
2078
public operations is that we can easily change their interface without
2079
any dramatic consequences in the rest of the code.
2083
Consider, again, the program without AspectJ. Suppose, for example,
2084
that at some point later the requirements for tracing change, stating
2085
that the trace messages should always include the string representation
2086
of the object whose methods are being traced. This can be achieved in
2087
at least two ways. One way is keep the interface of the methods
2088
<literal>traceEntry</literal> and <literal>traceExit</literal> as it
2092
<programlisting><![CDATA[
2093
public static void traceEntry(String str);
2094
public static void traceExit(String str);
2095
]]></programlisting>
2098
In this case, the caller is responsible for ensuring that the string
2099
representation of the object is part of the string given as argument.
2100
So, calls must look like:
2103
<programlisting><![CDATA[
2104
Trace.traceEntry("Square.distance in " + toString());
2105
]]></programlisting>
2108
Another way is to enforce the requirement with a second argument in the
2109
trace operations, e.g.
2112
<programlisting><![CDATA[
2113
public static void traceEntry(String str, Object obj);
2114
public static void traceExit(String str, Object obj);
2115
]]></programlisting>
2118
In this case, the caller is still responsible for sending the right
2119
object, but at least there is some guarantees that some object will be
2120
passed. The calls will look like:
2123
<programlisting><![CDATA[
2124
Trace.traceEntry("Square.distance", this);
2125
]]></programlisting>
2128
In either case, this change to the requirements of tracing will have
2129
dramatic consequences in the rest of the code -- every call to the
2130
trace operations traceEntry and traceExit must be changed!
2134
Here's another advantage of doing tracing with an aspect. We've already
2135
seen that in version 2 <literal>traceEntry</literal> and
2136
<literal>traceExit</literal> are not publicly exposed. So changing
2137
their interfaces, or the way they are used, has only a small effect
2138
inside the <classname>Trace</classname> class. Here's a partial view at
2139
the implementation of <classname>Trace</classname>, version 3. The
2140
differences with respect to version 2 are stressed in the
2144
<programlisting><![CDATA[
2145
abstract aspect Trace {
2147
public static int TRACELEVEL = 0;
2148
protected static PrintStream stream = null;
2149
protected static int callDepth = 0;
2151
public static void initStream(PrintStream s) {
2155
protected static void traceEntry(String str, Object o) {
2156
if (TRACELEVEL == 0) return;
2157
if (TRACELEVEL == 2) callDepth++;
2158
printEntering(str + ": " + o.toString());
2161
protected static void traceExit(String str, Object o) {
2162
if (TRACELEVEL == 0) return;
2163
printExiting(str + ": " + o.toString());
2164
if (TRACELEVEL == 2) callDepth--;
2167
private static void printEntering(String str) {
2169
stream.println("Entering " + str);
2172
private static void printExiting(String str) {
2174
stream.println("Exiting " + str);
2178
private static void printIndent() {
2179
for (int i = 0; i < callDepth; i++)
2184
abstract pointcut myClass(Object obj);
2186
pointcut myConstructor(Object obj): myClass(obj) && execution(new(..));
2187
pointcut myMethod(Object obj): myClass(obj) &&
2188
execution(* *(..)) && !execution(String toString());
2190
before(Object obj): myConstructor(obj) {
2191
traceEntry("" + thisJoinPointStaticPart.getSignature(), obj);
2193
after(Object obj): myConstructor(obj) {
2194
traceExit("" + thisJoinPointStaticPart.getSignature(), obj);
2197
before(Object obj): myMethod(obj) {
2198
traceEntry("" + thisJoinPointStaticPart.getSignature(), obj);
2200
after(Object obj): myMethod(obj) {
2201
traceExit("" + thisJoinPointStaticPart.getSignature(), obj);
2204
]]></programlisting>
2207
As you can see, we decided to apply the first design by preserving the
2208
interface of the methods <literal>traceEntry</literal> and
2209
<literal>traceExit</literal>. But it doesn't matter—we could as
2210
easily have applied the second design (the code in the directory
2211
<filename>examples/tracing/version3</filename> has the second design).
2212
The point is that the effects of this change in the tracing
2213
requirements are limited to the <classname>Trace</classname> aspect
2218
One implementation change worth noticing is the specification of the
2219
pointcuts. They now expose the object. To maintain full consistency
2220
with the behavior of version 2, we should have included tracing for
2221
static methods, by defining another pointcut for static methods and
2222
advising it. We leave that as an exercise.
2226
Moreover, we had to exclude the execution join point of the method
2227
<filename>toString</filename> from the <literal>methods</literal>
2228
pointcut. The problem here is that <literal>toString</literal> is being
2229
called from inside the advice. Therefore if we trace it, we will end
2230
up in an infinite recursion of calls. This is a subtle point, and one
2231
that you must be aware when writing advice. If the advice calls back to
2232
the objects, there is always the possibility of recursion. Keep that in
2237
In fact, esimply excluding the execution join point may not be enough,
2238
if there are calls to other traced methods within it -- in which case,
2239
the restriction should be
2242
<programlisting><![CDATA[
2243
&& !cflow(execution(String toString()))
2244
]]></programlisting>
2247
excluding both the execution of toString methods and all join points
2248
under that execution.
2252
In summary, to implement the change in the tracing requirements we had
2253
to make a couple of changes in the implementation of the
2254
<classname>Trace</classname> aspect class, including changing the
2255
specification of the pointcuts. That's only natural. But the
2256
implementation changes were limited to this aspect. Without aspects, we
2257
would have to change the implementation of every application class.
2261
Finally, to run this version of tracing, go to the directory
2262
<filename>examples</filename> and type:
2265
<programlisting><![CDATA[
2266
ajc -argfile tracing/tracev3.lst
2267
]]></programlisting>
2270
The file tracev3.lst lists the application classes as well as this
2271
version of the files <filename>Trace.java</filename> and
2272
<filename>TraceMyClasses.java</filename>. To run the program, type
2275
<programlisting><![CDATA[
2276
java tracing.version3.TraceMyClasses
2277
]]></programlisting>
2279
<para>The output should be:</para>
2281
<programlisting><![CDATA[
2282
--> tracing.TwoDShape(double, double)
2283
<-- tracing.TwoDShape(double, double)
2284
--> tracing.Circle(double, double, double)
2285
<-- tracing.Circle(double, double, double)
2286
--> tracing.TwoDShape(double, double)
2287
<-- tracing.TwoDShape(double, double)
2288
--> tracing.Circle(double, double, double)
2289
<-- tracing.Circle(double, double, double)
2290
--> tracing.Circle(double)
2291
<-- tracing.Circle(double)
2292
--> tracing.TwoDShape(double, double)
2293
<-- tracing.TwoDShape(double, double)
2294
--> tracing.Square(double, double, double)
2295
<-- tracing.Square(double, double, double)
2296
--> tracing.Square(double, double)
2297
<-- tracing.Square(double, double)
2298
--> double tracing.Circle.perimeter()
2299
<-- double tracing.Circle.perimeter()
2300
c1.perimeter() = 12.566370614359172
2301
--> double tracing.Circle.area()
2302
<-- double tracing.Circle.area()
2303
c1.area() = 12.566370614359172
2304
--> double tracing.Square.perimeter()
2305
<-- double tracing.Square.perimeter()
2306
s1.perimeter() = 4.0
2307
--> double tracing.Square.area()
2308
<-- double tracing.Square.area()
2310
--> double tracing.TwoDShape.distance(TwoDShape)
2311
--> double tracing.TwoDShape.getX()
2312
<-- double tracing.TwoDShape.getX()
2313
--> double tracing.TwoDShape.getY()
2314
<-- double tracing.TwoDShape.getY()
2315
<-- double tracing.TwoDShape.distance(TwoDShape)
2316
c2.distance(c1) = 4.242640687119285
2317
--> double tracing.TwoDShape.distance(TwoDShape)
2318
--> double tracing.TwoDShape.getX()
2319
<-- double tracing.TwoDShape.getX()
2320
--> double tracing.TwoDShape.getY()
2321
<-- double tracing.TwoDShape.getY()
2322
<-- double tracing.TwoDShape.distance(TwoDShape)
2323
s1.distance(c1) = 2.23606797749979
2324
--> String tracing.Square.toString()
2325
--> String tracing.TwoDShape.toString()
2326
<-- String tracing.TwoDShape.toString()
2327
<-- String tracing.Square.toString()
2328
s1.toString(): Square side = 1.0 @ (1.0, 2.0)
2329
]]></programlisting>
2338
compile-command: "java sax.SAXCount -v progguide.xml && java com.icl.saxon.StyleSheet -w0 progguide.xml progguide.html.xsl"
2340
sgml-local-ecat-files: "progguide.ced"
2341
sgml-parent-document:("progguide.xml" "book" "chapter")