aboutsummaryrefslogtreecommitdiffstats
path: root/docs/progguide/examples.adoc
diff options
context:
space:
mode:
authorAlexander Kriegisch <Alexander@Kriegisch.name>2022-01-08 11:50:55 +0700
committerAlexander Kriegisch <Alexander@Kriegisch.name>2024-01-06 10:09:11 +0100
commitd4a6906b3012fac6e4dbaca5854fc59ba0d67e47 (patch)
treec78540555837c12cfaef7f9cc95842668a3ee7f9 /docs/progguide/examples.adoc
parent9735e858af48ff0bce152ea489800a86a151b08d (diff)
downloadaspectj-d4a6906b3012fac6e4dbaca5854fc59ba0d67e47.tar.gz
aspectj-d4a6906b3012fac6e4dbaca5854fc59ba0d67e47.zip
Rename '*GuideDB' directories to their actual HTML site target names
Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
Diffstat (limited to 'docs/progguide/examples.adoc')
-rw-r--r--docs/progguide/examples.adoc1918
1 files changed, 1918 insertions, 0 deletions
diff --git a/docs/progguide/examples.adoc b/docs/progguide/examples.adoc
new file mode 100644
index 000000000..f01ae46d8
--- /dev/null
+++ b/docs/progguide/examples.adoc
@@ -0,0 +1,1918 @@
+[[examples]]
+== Examples
+
+[[examples-intro]]
+=== Introduction
+
+This chapter consists entirely of examples of AspectJ use.
+
+The examples can be grouped into four categories:
+
+technique::
+ Examples which illustrate how to use one or more features of the language
+development::
+ Examples of using AspectJ during the development phase of a project
+production::
+ Examples of using AspectJ to provide functionality in an application
+reusable::
+ Examples of reuse of aspects and pointcuts
+
+[[examples-howto]]
+=== Obtaining, Compiling and Running the Examples
+
+The examples source code is part of the AspectJ distribution which may
+be downloaded from the https://eclipse.org/aspectj[AspectJ project page].
+
+Compiling most examples is straightforward. Go the `InstallDir/examples`
+directory, and look for a `.lst` file in one of the example
+subdirectories. Use the `-arglist` option to `ajc` to compile the
+example. For instance, to compile the telecom example with billing, type
+
+[source, text]
+....
+ajc -argfile telecom/billing.lst
+....
+
+To run the examples, your classpath must include the AspectJ run-time
+Java archive (`aspectjrt.jar`). You may either set the `CLASSPATH`
+environment variable or use the `-classpath` command line option to the
+Java interpreter:
+
+[source, text]
+....
+(In Unix use a : in the CLASSPATH)
+java -classpath ".:InstallDir/lib/aspectjrt.jar" telecom.billingSimulation
+....
+
+[source, text]
+....
+(In Windows use a ; in the CLASSPATH)
+java -classpath ".;InstallDir/lib/aspectjrt.jar" telecom.billingSimulation
+....
+
+[[examples-basic]]
+=== Basic Techniques
+
+This section presents two basic techniques of using AspectJ, one each
+from the two fundamental ways of capturing crosscutting concerns: with
+dynamic join points and advice, and with static introduction. Advice
+changes an application's behavior. Introduction changes both an
+application's behavior and its structure.
+
+The first example, xref:#examples-joinPoints[Join Points and `thisJoinPoint`], is about
+gathering and using information about the join point that has triggered
+some advice. The second example, xref:#examples-roles[Roles and Views],
+concerns a crosscutting view of an existing class hierarchy.
+
+[[examples-joinPoints]]
+==== Join Points and `thisJoinPoint`
+
+(The code for this example is in `InstallDir/examples/tjp`.)
+
+A join point is some point in the execution of a program together with a
+view into the execution context when that point occurs. Join points are
+picked out by pointcuts. When a program reaches a join point, advice on
+that join point may run in addition to (or instead of) the join point
+itself.
+
+When using a pointcut that picks out join points of a single kind by
+name, typicaly the the advice will know exactly what kind of join point
+it is associated with. The pointcut may even publish context about the
+join point. Here, for example, since the only join points picked out by
+the pointcut are calls of a certain method, we can get the target value
+and one of the argument values of the method calls directly.
+
+[source, java]
+....
+before(Point p, int x):
+ target(p) &&
+ args(x) &&
+ call(void setX(int))
+{
+ if (!p.assertX(x))
+ System.out.println("Illegal value for x"); return;
+}
+....
+
+But sometimes the shape of the join point is not so clear. For instance,
+suppose a complex application is being debugged, and we want to trace
+when any method of some class is executed. The pointcut
+
+[source, java]
+....
+pointcut execsInProblemClass():
+ within(ProblemClass) &&
+ execution(* *(..));
+....
+
+will pick out each execution join point of every method defined within
+`ProblemClass`. Since advice executes at each join point picked out by
+the pointcut, we can reasonably ask which join point was reached.
+
+Information about the join point that was matched is available to advice
+through the special variable `thisJoinPoint`, of type
+xref:../api/org/aspectj/lang/JoinPoint.html[`org.aspectj.lang.JoinPoint`].
+Through this object we can access information such as
+
+* the kind of join point that was matched
+* the source location of the code associated with the join point
+* normal, short and long string representations of the current join
+point
+* the actual argument values of the join point
+* the signature of the member associated with the join point
+* the currently executing object
+* the target object
+* an object encapsulating the static information about the join point.
+This is also available through the special variable `thisJoinPointStaticPart`.
+
+===== The `Demo` class
+
+The class `tjp.Demo` in `tjp/Demo.java` defines two methods `foo` and
+`bar` with different parameter lists and return types. Both are called,
+with suitable arguments, by ``Demo``'s `go` method which was invoked from
+within its `main` method.
+
+[source, java]
+....
+public class Demo {
+ static Demo d;
+
+ public static void main(String[] args) {
+ new Demo().go();
+ }
+
+ void go() {
+ d = new Demo();
+ d.foo(1,d);
+ System.out.println(d.bar(new Integer(3)));
+ }
+
+ void foo(int i, Object o) {
+ System.out.println("Demo.foo(" + i + ", " + o + ")\n");
+ }
+
+ String bar (Integer j) {
+ System.out.println("Demo.bar(" + j + ")\n");
+ return "Demo.bar(" + j + ")";
+ }
+}
+....
+
+===== The `GetInfo` aspect
+
+This aspect uses around advice to intercept the execution of methods
+`foo` and `bar` in `Demo`, and prints out information garnered from
+`thisJoinPoint` to the console.
+
+[source, java]
+....
+aspect GetInfo {
+
+ static final void println(String s){ System.out.println(s); }
+
+ pointcut goCut(): cflow(this(Demo) && execution(void go()));
+
+ pointcut demoExecs(): within(Demo) && execution(* *(..));
+
+ Object around(): demoExecs() && !execution(* go()) && goCut() {
+ println("Intercepted message: " +
+ thisJoinPointStaticPart.getSignature().getName());
+ println("in class: " +
+ thisJoinPointStaticPart.getSignature().getDeclaringType().getName());
+ printParameters(thisJoinPoint);
+ println("Running original method: \n" );
+ Object result = proceed();
+ println(" result: " + result );
+ return result;
+ }
+
+ static private void printParameters(JoinPoint jp) {
+ println("Arguments: " );
+ Object[] args = jp.getArgs();
+ String[] names = ((CodeSignature)jp.getSignature()).getParameterNames();
+ Class[] types = ((CodeSignature)jp.getSignature()).getParameterTypes();
+ for (int i = 0; i < args.length; i++) {
+ println(
+ " " + i + ". " + names[i] +
+ " : " + types[i].getName() +
+ " = " + args[i]);
+ }
+ }
+}
+....
+
+====== Defining the scope of a pointcut
+
+The pointcut `goCut` is defined as
+
+[source, java]
+....
+cflow(this(Demo)) && execution(void go())
+....
+
+so that only executions made in the control flow of `Demo.go` are
+intercepted. The control flow from the method `go` includes the
+execution of `go` itself, so the definition of the around advice
+includes `!execution(* go())` to exclude it from the set of executions
+advised.
+
+====== Printing the class and method name
+
+The name of the method and that method's defining class are available as
+parts of the
+xref:../api/org/aspectj/lang/Signature.html[`org.aspectj.lang.Signature`]
+object returned by calling `getSignature()` on either `thisJoinPoint` or
+`thisJoinPointStaticPart`.
+
+====== Printing the parameters
+
+The static portions of the parameter details, the name and types of the
+parameters, can be accessed through the
+xref:../api/org/aspectj/lang/reflect/CodeSignature.html[`org.aspectj.lang.reflect.CodeSignature`]
+associated with the join point. All execution join points have code
+signatures, so the cast to `CodeSignature` cannot fail.
+
+The dynamic portions of the parameter details, the actual values of the
+parameters, are accessed directly from the execution join point object.
+
+[[examples-roles]]
+==== Roles and Views
+
+(The code for this example is in `InstallDir/examples/introduction`.)
+
+Like advice, inter-type declarations are members of an aspect. They
+declare members that act as if they were defined on another class.
+Unlike advice, inter-type declarations affect not only the behavior of
+the application, but also the structural relationship between an
+application's classes.
+
+This is crucial: Publically affecting the class structure of an
+application makes these modifications available to other components of
+the application.
+
+Aspects can declare inter-type
+
+* fields
+* methods
+* constructors
+
+and can also declare that target types
+
+* implement new interfaces
+* extend new classes
+
+This example provides three illustrations of the use of inter-type
+declarations to encapsulate roles or views of a class. The class our
+aspect will be dealing with, `Point`, is a simple class with rectangular
+and polar coordinates. Our inter-type declarations will make the class
+`Point`, in turn, cloneable, hashable, and comparable. These facilities
+are provided by AspectJ without having to modify the code for the class
+`Point`.
+
+===== The `Point` class
+
+The `Point` class defines geometric points whose interface includes
+polar and rectangular coordinates, plus some simple operations to
+relocate points. ``Point``'s implementation has attributes for both its
+polar and rectangular coordinates, plus flags to indicate which
+currently reflect the position of the point. Some operations cause the
+polar coordinates to be updated from the rectangular, and some have the
+opposite effect. This implementation, which is in intended to give the
+minimum number of conversions between coordinate systems, has the
+property that not all the attributes stored in a `Point` object are
+necessary to give a canonical representation such as might be used for
+storing, comparing, cloning or making hash codes from points. Thus the
+aspects, though simple, are not totally trivial.
+
+The diagram below gives an overview of the aspects and their interaction
+with the class `Point`.
+
+image:aspects.gif[image]
+
+===== The `CloneablePoint` aspect
+
+This first aspect is responsible for ``Point``'s implementation of the
+`Cloneable` interface. It declares that `Point implements Cloneable`
+with a `declare parents` form, and also publically declares a
+specialized ``Point``'s `clone()` method. In Java, all objects inherit the
+method `clone` from the class `Object`, but an object is not cloneable
+unless its class also implements the interface `Cloneable`. In addition,
+classes frequently have requirements over and above the simple
+bit-for-bit copying that `Object.clone` does. In our case, we want to
+update a ``Point``'s coordinate systems before we actually clone the
+`Point`. So our aspect makes sure that `Point` overrides `Object.clone`
+with a new method that does what we want.
+
+We also define a test `main` method in the aspect for convenience.
+
+[source, java]
+....
+public aspect CloneablePoint {
+
+ declare parents: Point implements Cloneable;
+
+ public Object Point.clone() throws CloneNotSupportedException {
+ // we choose to bring all fields up to date before cloning.
+ makeRectangular();
+ makePolar();
+ return super.clone();
+ }
+
+ public static void main(String[] args) {
+ Point p1 = new Point();
+ Point p2 = null;
+
+ p1.setPolar(Math.PI, 1.0);
+ try {
+ p2 = (Point)p1.clone();
+ } catch (CloneNotSupportedException e) {}
+ System.out.println("p1 =" + p1);
+ System.out.println("p2 =" + p2);
+
+ p1.rotate(Math.PI / -2);
+ System.out.println("p1 =" + p1);
+ System.out.println("p2 =" + p2);
+ }
+}
+....
+
+===== The `ComparablePoint` aspect
+
+`ComparablePoint` is responsible for ``Point``'s implementation of the
+`Comparable` interface.
+
+The interface `Comparable` defines the single method `compareTo` which
+can be use to define a natural ordering relation among the objects of a
+class that implement it.
+
+`ComparablePoint` uses `declare parents` to declare that `Point implements Comparable`,
+and also publically declares the appropriate `compareTo(Object)` method:
+A `Point` `p1` is said to be less than another `Point p2` if `p1` is closer to the origin.
+
+We also define a test `main` method in the aspect for convenience.
+
+[source, java]
+....
+public aspect ComparablePoint {
+
+ declare parents: Point implements Comparable;
+
+ public int Point.compareTo(Object o) {
+ return (int) (this.getRho() - ((Point)o).getRho());
+ }
+
+ public static void main(String[] args) {
+ Point p1 = new Point();
+ Point p2 = new Point();
+
+ System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
+
+ p1.setRectangular(2,5);
+ p2.setRectangular(2,5);
+ System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
+
+ p2.setRectangular(3,6);
+ System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
+
+ p1.setPolar(Math.PI, 4);
+ p2.setPolar(Math.PI, 4);
+ System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
+
+ p1.rotate(Math.PI / 4.0);
+ System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
+
+ p1.offset(1,1);
+ System.out.println("p1 =?= p2 :" + p1.compareTo(p2));
+ }
+}
+....
+
+===== The `HashablePoint` aspect
+
+Our third aspect is responsible for ``Point``'s overriding of ``Object``'s
+`equals` and `hashCode` methods in order to make ``Point``s hashable.
+
+The method `Object.hashCode` returns an integer, suitable for use as a
+hash table key. It is not required that two objects which are not equal
+(according to the `equals` method) return different integer results from
+`hashCode` but it can improve performance when the integer is used as a
+key into a data structure. However, any two objects which are equal must
+return the same integer value from a call to `hashCode`. Since the
+default implementation of `Object.equals` returns `true` only when two
+objects are identical, we need to redefine both `equals` and `hashCode`
+to work correctly with objects of type `Point`. For example, we want two
+`Point` objects to test equal when they have the same `x` and `y`
+values, or the same `rho` and `theta` values, not just when they refer
+to the same object. We do this by overriding the methods `equals` and
+`hashCode` in the class `Point`.
+
+So `HashablePoint` declares ``Point``'s `hashCode` and `equals` methods,
+using ``Point``'s rectangular coordinates to generate a hash code and to
+test for equality. The `x` and `y` coordinates are obtained using the
+appropriate get methods, which ensure the rectangular coordinates are
+up-to-date before returning their values.
+
+And again, we supply a `main` method in the aspect for testing.
+
+[source, java]
+....
+public aspect HashablePoint {
+
+ public int Point.hashCode() {
+ return (int) (getX() + getY() % Integer.MAX_VALUE);
+ }
+
+ public boolean Point.equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof Point)) return false;
+ Point other = (Point)o;
+ return (getX() == other.getX()) && (getY() == other.getY());
+ }
+
+ public static void main(String[] args) {
+ Hashtable h = new Hashtable();
+ Point p1 = new Point();
+
+ p1.setRectangular(10, 10);
+ Point p2 = new Point();
+
+ p2.setRectangular(10, 10);
+
+ System.out.println("p1 = " + p1);
+ System.out.println("p2 = " + p2);
+ System.out.println("p1.hashCode() = " + p1.hashCode());
+ System.out.println("p2.hashCode() = " + p2.hashCode());
+
+ h.put(p1, "P1");
+ System.out.println("Got: " + h.get(p2));
+ }
+}
+....
+
+[[examples-development]]
+=== Development Aspects
+
+==== Tracing using aspects
+
+(The code for this example is in `InstallDir/examples/tracing`.)
+
+Writing a class that provides tracing functionality is easy: a couple of
+functions, a boolean flag for turning tracing on and off, a choice for
+an output stream, maybe some code for formatting the output -- these are
+all elements that `Trace` classes have been known to have. `Trace`
+classes may be highly sophisticated, too, if the task of tracing the
+execution of a program demands it.
+
+But developing the support for tracing is just one part of the effort of
+inserting tracing into a program, and, most likely, not the biggest
+part. The other part of the effort is calling the tracing functions at
+appropriate times. In large systems, this interaction with the tracing
+support can be overwhelming. Plus, tracing is one of those things that
+slows the system down, so these calls should often be pulled out of the
+system before the product is shipped. For these reasons, it is not
+unusual for developers to write ad-hoc scripting programs that rewrite
+the source code by inserting/deleting trace calls before and after the
+method bodies.
+
+AspectJ can be used for some of these tracing concerns in a less ad-hoc
+way. Tracing can be seen as a concern that crosscuts the entire system
+and as such is amenable to encapsulation in an aspect. In addition, it
+is fairly independent of what the system is doing. Therefore tracing is
+one of those kind of system aspects that can potentially be plugged in
+and unplugged without any side-effects in the basic functionality of the
+system.
+
+===== An Example Application
+
+Throughout this example we will use a simple application that contains
+only four classes. The application is about shapes. The `TwoDShape`
+class is the root of the shape hierarchy:
+
+[source, java]
+....
+public abstract class TwoDShape {
+ protected double x, y;
+ protected TwoDShape(double x, double y) {
+ this.x = x; this.y = y;
+ }
+ public double getX() { return x; }
+ public double getY() { return y; }
+ public double distance(TwoDShape s) {
+ double dx = Math.abs(s.getX() - x);
+ double dy = Math.abs(s.getY() - y);
+ return Math.sqrt(dx*dx + dy*dy);
+ }
+ public abstract double perimeter();
+ public abstract double area();
+ public String toString() {
+ return (" @ (" + String.valueOf(x) + ", " + String.valueOf(y) + ") ");
+ }
+}
+....
+
+`TwoDShape` has two subclasses, `Circle` and `Square`:
+
+[source, java]
+....
+public class Circle extends TwoDShape {
+ protected double r;
+ public Circle(double x, double y, double r) {
+ super(x, y); this.r = r;
+ }
+ public Circle(double x, double y) { this( x, y, 1.0); }
+ public Circle(double r) { this(0.0, 0.0, r); }
+ public Circle() { this(0.0, 0.0, 1.0); }
+ public double perimeter() {
+ return 2 * Math.PI * r;
+ }
+ public double area() {
+ return Math.PI * r*r;
+ }
+ public String toString() {
+ return ("Circle radius = " + String.valueOf(r) + super.toString());
+ }
+}
+....
+
+[source, java]
+....
+public class Square extends TwoDShape {
+ protected double s; // side
+ public Square(double x, double y, double s) {
+ super(x, y); this.s = s;
+ }
+ public Square(double x, double y) { this( x, y, 1.0); }
+ public Square(double s) { this(0.0, 0.0, s); }
+ public Square() { this(0.0, 0.0, 1.0); }
+ public double perimeter() {
+ return 4 * s;
+ }
+ public double area() {
+ return s*s;
+ }
+ public String toString() {
+ return ("Square side = " + String.valueOf(s) + super.toString());
+ }
+}
+....
+
+To run this application, compile the classes. You can do it with or
+without ajc, the AspectJ compiler. If you've installed AspectJ, go to
+the directory `InstallDir/examples` and type:
+
+[source, text]
+....
+ajc -argfile tracing/notrace.lst
+....
+
+To run the program, type
+
+[source, text]
+....
+java tracing.ExampleMain
+....
+
+(we don't need anything special on the classpath since this is pure Java
+code). You should see the following output:
+
+[source, text]
+....
+c1.perimeter() = 12.566370614359172
+c1.area() = 12.566370614359172
+s1.perimeter() = 4.0
+s1.area() = 1.0
+c2.distance(c1) = 4.242640687119285
+s1.distance(c1) = 2.23606797749979
+s1.toString(): Square side = 1.0 @ (1.0, 2.0)
+....
+
+===== Tracing - Version 1
+
+In a first attempt to insert tracing in this application, we will start
+by writing a `Trace` class that is exactly what we would write if we
+didn't have aspects. The implementation is in `version1/Trace.java`. Its
+public interface is:
+
+[source, java]
+....
+public class Trace {
+ public static int TRACELEVEL = 0;
+ public static void initStream(PrintStream s) {...}
+ public static void traceEntry(String str) {...}
+ public static void traceExit(String str) {...}
+}
+....
+
+If we didn't have AspectJ, we would have to insert calls to `traceEntry`
+and `traceExit` in all methods and constructors we wanted to trace, and
+to initialize `TRACELEVEL` and the stream. If we wanted to trace all the
+methods and constructors in our example, that would amount to around 40
+calls, and we would hope we had not forgotten any method. But we can do
+that more consistently and reliably with the following aspect (found in
+`version1/TraceMyClasses.java`):
+
+[source, java]
+....
+public aspect TraceMyClasses {
+ pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);
+ pointcut myConstructor(): myClass() && execution(new(..));
+ pointcut myMethod(): myClass() && execution(* *(..));
+
+ before (): myConstructor() {
+ Trace.traceEntry("" + thisJoinPointStaticPart.getSignature());
+ }
+ after(): myConstructor() {
+ Trace.traceExit("" + thisJoinPointStaticPart.getSignature());
+ }
+
+ before (): myMethod() {
+ Trace.traceEntry("" + thisJoinPointStaticPart.getSignature());
+ }
+ after(): myMethod() {
+ Trace.traceExit("" + thisJoinPointStaticPart.getSignature());
+ }
+}
+....
+
+This aspect performs the tracing calls at appropriate times. According
+to this aspect, tracing is performed at the entrance and exit of every
+method and constructor defined within the shape hierarchy.
+
+What is printed at before and after each of the traced join points is
+the signature of the method executing. Since the signature is static
+information, we can get it through `thisJoinPointStaticPart`.
+
+To run this version of tracing, go to the directory
+`InstallDir/examples` and type:
+
+[source, text]
+....
+ajc -argfile tracing/tracev1.lst
+....
+
+Running the main method of `tracing.version1.TraceMyClasses` should
+produce the output:
+
+[source, text]
+....
+ --> tracing.TwoDShape(double, double)
+ <-- tracing.TwoDShape(double, double)
+ --> tracing.Circle(double, double, double)
+ <-- tracing.Circle(double, double, double)
+ --> tracing.TwoDShape(double, double)
+ <-- tracing.TwoDShape(double, double)
+ --> tracing.Circle(double, double, double)
+ <-- tracing.Circle(double, double, double)
+ --> tracing.Circle(double)
+ <-- tracing.Circle(double)
+ --> tracing.TwoDShape(double, double)
+ <-- tracing.TwoDShape(double, double)
+ --> tracing.Square(double, double, double)
+ <-- tracing.Square(double, double, double)
+ --> tracing.Square(double, double)
+ <-- tracing.Square(double, double)
+ --> double tracing.Circle.perimeter()
+ <-- double tracing.Circle.perimeter()
+c1.perimeter() = 12.566370614359172
+ --> double tracing.Circle.area()
+ <-- double tracing.Circle.area()
+c1.area() = 12.566370614359172
+ --> double tracing.Square.perimeter()
+ <-- double tracing.Square.perimeter()
+s1.perimeter() = 4.0
+ --> double tracing.Square.area()
+ <-- double tracing.Square.area()
+s1.area() = 1.0
+ --> double tracing.TwoDShape.distance(TwoDShape)
+ --> double tracing.TwoDShape.getX()
+ <-- double tracing.TwoDShape.getX()
+ --> double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.distance(TwoDShape)
+c2.distance(c1) = 4.242640687119285
+ --> double tracing.TwoDShape.distance(TwoDShape)
+ --> double tracing.TwoDShape.getX()
+ <-- double tracing.TwoDShape.getX()
+ --> double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.distance(TwoDShape)
+s1.distance(c1) = 2.23606797749979
+ --> String tracing.Square.toString()
+ --> String tracing.TwoDShape.toString()
+ <-- String tracing.TwoDShape.toString()
+ <-- String tracing.Square.toString()
+s1.toString(): Square side = 1.0 @ (1.0, 2.0)
+....
+
+When `TraceMyClasses.java` is not provided to `ajc`, the aspect does not
+have any affect on the system and the tracing is unplugged.
+
+===== Tracing - Version 2
+
+Another way to accomplish the same thing would be to write a reusable
+tracing aspect that can be used not only for these application classes,
+but for any class. One way to do this is to merge the tracing
+functionality of `Trace - version1` with the crosscutting support of
+`TraceMyClasses - version1`. We end up with a `Trace` aspect (found in
+`version2/Trace.java`) with the following public interface
+
+[source, java]
+....
+abstract aspect Trace {
+ public static int TRACELEVEL = 2;
+ public static void initStream(PrintStream s) {...}
+ protected static void traceEntry(String str) {...}
+ protected static void traceExit(String str) {...}
+ abstract pointcut myClass();
+}
+....
+
+In order to use it, we need to define our own subclass that knows about
+our application classes, in `version2/TraceMyClasses.java`:
+
+[source, java]
+....
+public aspect TraceMyClasses extends Trace {
+ pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square);
+
+ public static void main(String[] args) {
+ Trace.TRACELEVEL = 2;
+ Trace.initStream(System.err);
+ ExampleMain.main(args);
+ }
+}
+....
+
+Notice that we've simply made the pointcut `classes`, that was an
+abstract pointcut in the super-aspect, concrete. To run this version of
+tracing, go to the directory `examples` and type:
+
+[source, text]
+....
+ajc -argfile tracing/tracev2.lst
+....
+
+The file `tracev2.lst` lists the application classes as well as this
+version of the files Trace.java and TraceMyClasses.java. Running the
+main method of `tracing.version2.TraceMyClasses` should output exactly
+the same trace information as that from version 1.
+
+The entire implementation of the new `Trace` class is:
+
+[source, java]
+....
+abstract aspect Trace {
+
+ // implementation part
+
+ public static int TRACELEVEL = 2;
+ protected static PrintStream stream = System.err;
+ protected static int callDepth = 0;
+
+ public static void initStream(PrintStream s) {
+ stream = s;
+ }
+ protected static void traceEntry(String str) {
+ if (TRACELEVEL == 0) return;
+ if (TRACELEVEL == 2) callDepth++;
+ printEntering(str);
+ }
+ protected static void traceExit(String str) {
+ if (TRACELEVEL == 0) return;
+ printExiting(str);
+ if (TRACELEVEL == 2) callDepth--;
+ }
+ private static void printEntering(String str) {
+ printIndent();
+ stream.println("--> " + str);
+ }
+ private static void printExiting(String str) {
+ printIndent();
+ stream.println("<-- " + str);
+ }
+ private static void printIndent() {
+ for (int i = 0; i < callDepth; i++)
+ stream.print(" ");
+ }
+
+ // protocol part
+
+ abstract pointcut myClass();
+
+ pointcut myConstructor(): myClass() && execution(new(..));
+ pointcut myMethod(): myClass() && execution(* *(..));
+
+ before(): myConstructor() {
+ traceEntry("" + thisJoinPointStaticPart.getSignature());
+ }
+ after(): myConstructor() {
+ traceExit("" + thisJoinPointStaticPart.getSignature());
+ }
+
+ before(): myMethod() {
+ traceEntry("" + thisJoinPointStaticPart.getSignature());
+ }
+ after(): myMethod() {
+ traceExit("" + thisJoinPointStaticPart.getSignature());
+ }
+}
+....
+
+This version differs from version 1 in several subtle ways. The first
+thing to notice is that this `Trace` class merges the functional part of
+tracing with the crosscutting of the tracing calls. That is, in version
+1, there was a sharp separation between the tracing support (the class
+`Trace`) and the crosscutting usage of it (by the class
+`TraceMyClasses`). In this version those two things are merged. That's
+why the description of this class explicitly says that "Trace messages
+are printed before and after constructors and methods are," which is
+what we wanted in the first place. That is, the placement of the calls,
+in this version, is established by the aspect class itself, leaving less
+opportunity for misplacing calls.
+
+A consequence of this is that there is no need for providing
+`traceEntry` and `traceExit` as public operations of this class. You can
+see that they were classified as protected. They are supposed to be
+internal implementation details of the advice.
+
+The key piece of this aspect is the abstract pointcut classes that
+serves as the base for the definition of the pointcuts constructors and
+methods. Even though `classes` is abstract, and therefore no concrete
+classes are mentioned, we can put advice on it, as well as on the
+pointcuts that are based on it. The idea is "we don't know exactly what
+the pointcut will be, but when we do, here's what we want to do with
+it." In some ways, abstract pointcuts are similar to abstract methods.
+Abstract methods don't provide the implementation, but you know that the
+concrete subclasses will, so you can invoke those methods.
+
+[[examples-production]]
+=== Production Aspects
+
+==== A Bean Aspect
+
+(The code for this example is in `InstallDir/examples/bean`.)
+
+This example examines an aspect that makes Point objects into Java beans
+with bound properties.
+
+Java beans are reusable software components that can be visually
+manipulated in a builder tool. The requirements for an object to be a
+bean are few. Beans must define a no-argument constructor and must be
+either `Serializable` or `Externalizable`. Any properties of the object
+that are to be treated as bean properties should be indicated by the
+presence of appropriate `get` and `set` methods whose names are
+`get__property__` and `set__property__` where `__property__` is the name of
+a field in the bean class. Some bean properties, known as bound
+properties, fire events whenever their values change so that any
+registered listeners (such as, other beans) will be informed of those
+changes. Making a bound property involves keeping a list of registered
+listeners, and creating and dispatching event objects in methods that
+change the property values, such as `set__property__` methods.
+
+`Point` is a simple class representing points with rectangular
+coordinates. `Point` does not know anything about being a bean: there
+are set methods for `x` and `y` but they do not fire events, and the
+class is not serializable. Bound is an aspect that makes `Point` a
+serializable class and makes its `get` and `set` methods support the
+bound property protocol.
+
+===== The `Point` class
+
+The `Point` class is a very simple class with trivial getters and
+setters, and a simple vector offset method.
+
+[source, java]
+....
+class Point {
+
+ protected int x = 0;
+ protected int y = 0;
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public void setRectangular(int newX, int newY) {
+ setX(newX);
+ setY(newY);
+ }
+
+ public void setX(int newX) {
+ x = newX;
+ }
+
+ public void setY(int newY) {
+ y = newY;
+ }
+
+ public void offset(int deltaX, int deltaY) {
+ setRectangular(x + deltaX, y + deltaY);
+ }
+
+ public String toString() {
+ return "(" + getX() + ", " + getY() + ")" ;
+ }
+}
+....
+
+===== The `BoundPoint` aspect
+
+The `BoundPoint` aspect is responsible for ``Point``'s "beanness". The
+first thing it does is privately declare that each `Point` has a
+`support` field that holds reference to an instance of
+`PropertyChangeSupport`.
+
+[source, java]
+....
+private PropertyChangeSupport Point.support = new PropertyChangeSupport(this);
+....
+
+The property change support object must be constructed with a reference
+to the bean for which it is providing support, so it is initialized by
+passing it `this`, an instance of `Point`. Since the `support` field is
+private declared in the aspect, only the code in the aspect can refer to
+it.
+
+The aspect also declares ``Point``'s methods for registering and managing
+listeners for property change events, which delegate the work to the
+property change support object:
+
+[source, java]
+....
+public void Point.addPropertyChangeListener(PropertyChangeListener listener){
+ support.addPropertyChangeListener(listener);
+}
+public void Point.addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ support.addPropertyChangeListener(propertyName, listener);
+}
+public void Point.removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ support.removePropertyChangeListener(propertyName, listener);
+}
+public void Point.removePropertyChangeListener(PropertyChangeListener listener) {
+ support.removePropertyChangeListener(listener);
+}
+public void Point.hasListeners(String propertyName) {
+ support.hasListeners(propertyName);
+}
+....
+
+The aspect is also responsible for making sure `Point` implements the
+`Serializable` interface:
+
+[source, java]
+....
+declare parents: Point implements Serializable;
+....
+
+Implementing this interface in Java does not require any methods to be
+implemented. Serialization for `Point` objects is provided by the
+default serialization method.
+
+The `setters` pointcut picks out calls to the ``Point``'s `set` methods:
+any method whose name begins with "`set`" and takes one parameter. The
+around advice on `setters()` stores the values of the `X` and `Y`
+properties, calls the original `set` method and then fires the
+appropriate property change event according to which set method was
+called.
+
+[source, java]
+....
+aspect BoundPoint {
+ private PropertyChangeSupport Point.support = new PropertyChangeSupport(this);
+
+ public void Point.addPropertyChangeListener(PropertyChangeListener listener) {
+ support.addPropertyChangeListener(listener);
+ }
+ public void Point.addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ support.addPropertyChangeListener(propertyName, listener);
+ }
+ public void Point.removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
+ support.removePropertyChangeListener(propertyName, listener);
+ }
+ public void Point.removePropertyChangeListener(PropertyChangeListener listener) {
+ support.removePropertyChangeListener(listener);
+ }
+ public void Point.hasListeners(String propertyName) {
+ support.hasListeners(propertyName);
+ }
+
+ declare parents: Point implements Serializable;
+
+ pointcut setter(Point p): call(void Point.set*(*)) && target(p);
+
+ void around(Point p): setter(p) {
+ String propertyName =
+ thisJoinPointStaticPart.getSignature().getName().substring("set".length());
+ int oldX = p.getX();
+ int oldY = p.getY();
+ proceed(p);
+ if (propertyName.equals("X")){
+ firePropertyChange(p, propertyName, oldX, p.getX());
+ } else {
+ firePropertyChange(p, propertyName, oldY, p.getY());
+ }
+ }
+
+ void firePropertyChange(Point p, String property, double oldval, double newval) {
+ p.support.firePropertyChange(property, new Double(oldval), new Double(newval));
+ }
+}
+....
+
+===== The Test Program
+
+The test program registers itself as a property change listener to a
+`Point` object that it creates and then performs simple manipulation of
+that point: calling its set methods and the offset method. Then it
+serializes the point and writes it to a file and then reads it back. The
+result of saving and restoring the point is that a new point is created.
+
+[source, java]
+....
+class Demo implements PropertyChangeListener {
+
+ static final String fileName = "test.tmp";
+
+ public void propertyChange(PropertyChangeEvent e){
+ System.out.println(
+ "Property " + e.getPropertyName() + " changed from " +
+ e.getOldValue() + " to " + e.getNewValue()
+ );
+ }
+
+ public static void main(String[] args) {
+ Point p1 = new Point();
+ p1.addPropertyChangeListener(new Demo());
+ System.out.println("p1 =" + p1);
+ p1.setRectangular(5,2);
+ System.out.println("p1 =" + p1);
+ p1.setX( 6 );
+ p1.setY( 3 );
+ System.out.println("p1 =" + p1);
+ p1.offset(6,4);
+ System.out.println("p1 =" + p1);
+ save(p1, fileName);
+ Point p2 = (Point) restore(fileName);
+ System.out.println("Had: " + p1);
+ System.out.println("Got: " + p2);
+ }
+ // ...
+}
+....
+
+===== Compiling and Running the Example
+
+To compile and run this example, go to the examples directory and type:
+
+[source, text]
+....
+ajc -argfile bean/files.lst
+java bean.Demo
+....
+
+[[the-subject-observer-protocol]]
+==== The Subject/Observer Protocol
+
+(The code for this example is in `InstallDir/examples/observer`.)
+
+This demo illustrates how the Subject/Observer design pattern can be
+coded with aspects.
+
+The demo consists of the following: A colored label is a renderable
+object that has a color that cycles through a set of colors, and a
+number that records the number of cycles it has been through. A button
+is an action item that records when it is clicked.
+
+With these two kinds of objects, we can build up a Subject/Observer
+relationship in which colored labels observe the clicks of buttons; that
+is, where colored labels are the observers and buttons are the subjects.
+
+The demo is designed and implemented using the Subject/Observer design
+pattern. The remainder of this example explains the classes and aspects
+of this demo, and tells you how to run it.
+
+===== Generic Components
+
+The generic parts of the protocol are the interfaces `Subject` and
+`Observer`, and the abstract aspect `SubjectObserverProtocol`. The
+`Subject` interface is simple, containing methods to add, remove, and
+view `Observer` objects, and a method for getting data about state
+changes:
+
+[source, java]
+....
+interface Subject {
+ void addObserver(Observer obs);
+ void removeObserver(Observer obs);
+ Vector getObservers();
+ Object getData();
+}
+....
+
+The `Observer` interface is just as simple, with methods to set and get
+`Subject` objects, and a method to call when the subject gets updated.
+
+[source, java]
+....
+interface Observer {
+ void setSubject(Subject s);
+ Subject getSubject();
+ void update();
+}
+....
+
+The `SubjectObserverProtocol` aspect contains within it all of the
+generic parts of the protocol, namely, how to fire the `Observer`
+objects' update methods when some state changes in a subject.
+
+[source, java]
+....
+abstract aspect SubjectObserverProtocol {
+
+ abstract pointcut stateChanges(Subject s);
+
+ after(Subject s): stateChanges(s) {
+ for (int i = 0; i < s.getObservers().size(); i++) {
+ ((Observer)s.getObservers().elementAt(i)).update();
+ }
+ }
+
+ private Vector Subject.observers = new Vector();
+ public void Subject.addObserver(Observer obs) {
+ observers.addElement(obs);
+ obs.setSubject(this);
+ }
+ public void Subject.removeObserver(Observer obs) {
+ observers.removeElement(obs);
+ obs.setSubject(null);
+ }
+ public Vector Subject.getObservers() { return observers; }
+
+ private Subject Observer.subject = null;
+ public void Observer.setSubject(Subject s) { subject = s; }
+ public Subject Observer.getSubject() { return subject; }
+
+}
+....
+
+Note that this aspect does three things. It define an abstract pointcut
+that extending aspects can override. It defines advice that should run
+after the join points of the pointcut. And it declares an inter-type
+field and two inter-type methods so that each `Observer` can hold onto
+its `Subject`.
+
+===== Application Classes
+
+`Button` objects extend `java.awt.Button`, and all they do is make sure
+the `void click()` method is called whenever a button is clicked.
+
+[source, java]
+....
+class Button extends java.awt.Button {
+
+ static final Color defaultBackgroundColor = Color.gray;
+ static final Color defaultForegroundColor = Color.black;
+ static final String defaultText = "cycle color";
+
+ Button(Display display) {
+ super();
+ setLabel(defaultText);
+ setBackground(defaultBackgroundColor);
+ setForeground(defaultForegroundColor);
+ addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ Button.this.click();
+ }
+ });
+ display.addToFrame(this);
+ }
+
+ public void click() {}
+}
+....
+
+Note that this class knows nothing about being a Subject.
+
+ColorLabel objects are labels that support the void colorCycle() method.
+Again, they know nothing about being an observer.
+
+[source, java]
+....
+class ColorLabel extends Label {
+
+ ColorLabel(Display display) {
+ super();
+ display.addToFrame(this);
+ }
+
+ final static Color[] colors =
+ { Color.red, Color.blue, Color.green, Color.magenta };
+ private int colorIndex = 0;
+ private int cycleCount = 0;
+ void colorCycle() {
+ cycleCount++;
+ colorIndex = (colorIndex + 1) % colors.length;
+ setBackground(colors[colorIndex]);
+ setText("" + cycleCount);
+ }
+}
+....
+
+Finally, the `SubjectObserverProtocolImpl` implements the
+subject/observer protocol, with `Button` objects as subjects and
+`ColorLabel` objects as observers:
+
+[source, java]
+....
+package observer;
+
+import java.util.Vector;
+
+aspect SubjectObserverProtocolImpl extends SubjectObserverProtocol {
+
+ declare parents: Button implements Subject;
+ public Object Button.getData() { return this; }
+
+ declare parents: ColorLabel implements Observer;
+ public void ColorLabel.update() {
+ colorCycle();
+ }
+
+ pointcut stateChanges(Subject s):
+ target(s) &&
+ call(void Button.click());
+
+}
+....
+
+It does this by assuring that `Button` and `ColorLabel` implement the
+appropriate interfaces, declaring that they implement the methods
+required by those interfaces, and providing a definition for the
+abstract `stateChanges` pointcut. Now, every time a `Button` is clicked,
+all `ColorLabel` objects observing that button will `colorCycle`.
+
+===== Compiling and Running
+
+`Demo` is the top class that starts this demo. It instantiates a two
+buttons and three observers and links them together as subjects and
+observers. So to run the demo, go to the `examples` directory and type:
+
+[source, text]
+....
+ajc -argfile observer/files.lst
+java observer.Demo
+....
+
+==== A Simple Telecom Simulation
+
+(The code for this example is in `InstallDir/examples/telecom`.)
+
+This example illustrates some ways that dependent concerns can be
+encoded with aspects. It uses an example system comprising a simple
+model of telephone connections to which timing and billing features are
+added using aspects, where the billing feature depends upon the timing
+feature.
+
+===== The Application
+
+The example application is a simple simulation of a telephony system in
+which customers make, accept, merge and hang-up both local and long
+distance calls. The application architecture is in three layers.
+
+* The basic objects provide basic functionality to simulate customers,
+ calls and connections (regular calls have one connection, conference
+ calls have more than one).
+* The timing feature is concerned with timing the connections and
+ keeping the total connection time per customer. Aspects are used to add
+ a timer to each connection and to manage the total time per customer.
+* The billing feature is concerned with charging customers for the calls
+ they make. Aspects are used to calculate a charge per connection and,
+ upon termination of a connection, to add the charge to the appropriate
+ customer's bill. The billing aspect builds upon the timing aspect: it
+ uses a pointcut defined in Timing and it uses the timers that are
+ associated with connections.
+
+The simulation of system has three configurations: basic, timing and
+billing. Programs for the three configurations are in classes
+`BasicSimulation`, `TimingSimulation` and `BillingSimulation`. These
+share a common superclass `AbstractSimulation`, which defines the method
+run with the simulation itself and the method wait used to simulate
+elapsed time.
+
+===== The Basic Objects
+
+The telecom simulation comprises the classes `Customer`, `Call` and the
+abstract class `Connection` with its two concrete subclasses `Local` and
+`LongDistance`. Customers have a name and a numeric area code. They also
+have methods for managing calls. Simple calls are made between one
+customer (the caller) and another (the receiver), a `Connection` object
+is used to connect them. Conference calls between more than two
+customers will involve more than one connection. A customer may be
+involved in many calls at one time.
+
+image:telecom.gif[image]
+
+===== The `Customer` class
+
+`Customer` has methods `call`, `pickup`, `hangup` and `merge` for
+managing calls.
+
+[source, java]
+....
+public class Customer {
+
+ private String name;
+ private int areacode;
+ private Vector calls = new Vector();
+
+ protected void removeCall(Call c){
+ calls.removeElement(c);
+ }
+
+ protected void addCall(Call c){
+ calls.addElement(c);
+ }
+
+ public Customer(String name, int areacode) {
+ this.name = name;
+ this.areacode = areacode;
+ }
+
+ public String toString() {
+ return name + "(" + areacode + ")";
+ }
+
+ public int getAreacode(){
+ return areacode;
+ }
+
+ public boolean localTo(Customer other){
+ return areacode == other.areacode;
+ }
+
+ public Call call(Customer receiver) {
+ Call call = new Call(this, receiver);
+ addCall(call);
+ return call;
+ }
+
+ public void pickup(Call call) {
+ call.pickup();
+ addCall(call);
+ }
+
+ public void hangup(Call call) {
+ call.hangup(this);
+ removeCall(call);
+ }
+
+ public void merge(Call call1, Call call2){
+ call1.merge(call2);
+ removeCall(call2);
+ }
+}
+....
+
+===== The `Call` class
+
+Calls are created with a caller and receiver who are customers. If the
+caller and receiver have the same area code then the call can be
+established with a `Local` connection (see below), otherwise a
+`LongDistance` connection is required. A call comprises a number of
+connections between customers. Initially there is only the connection
+between the caller and receiver but additional connections can be added
+if calls are merged to form conference calls.
+
+===== The `Connection` class
+
+The class `Connection` models the physical details of establishing a
+connection between customers. It does this with a simple state machine
+(connections are initially `PENDING`, then `COMPLETED` and finally
+`DROPPED`). Messages are printed to the console so that the state of
+connections can be observed. Connection is an abstract class with two
+concrete subclasses: `Local` and `LongDistance`.
+
+[source, java]
+....
+abstract class Connection {
+
+ public static final int PENDING = 0;
+ public static final int COMPLETE = 1;
+ public static final int DROPPED = 2;
+
+ Customer caller, receiver;
+ private int state = PENDING;
+
+ Connection(Customer a, Customer b) {
+ this.caller = a;
+ this.receiver = b;
+ }
+
+ public int getState(){
+ return state;
+ }
+
+ public Customer getCaller() { return caller; }
+
+ public Customer getReceiver() { return receiver; }
+
+ void complete() {
+ state = COMPLETE;
+ System.out.println("connection completed");
+ }
+
+ void drop() {
+ state = DROPPED;
+ System.out.println("connection dropped");
+ }
+
+ public boolean connects(Customer c){
+ return (caller == c || receiver == c);
+ }
+
+}
+....
+
+===== The `Local` and `LongDistance` classes
+
+The two kinds of connections supported by our simulation are `Local` and
+`LongDistance` connections.
+
+[source, java]
+....
+class Local extends Connection {
+ Local(Customer a, Customer b) {
+ super(a, b);
+ System.out.println(
+ "[new local connection from " + a + " to " + b + "]"
+ );
+ }
+}
+....
+
+[source, java]
+....
+class LongDistance extends Connection {
+ LongDistance(Customer a, Customer b) {
+ super(a, b);
+ System.out.println(
+ "[new long distance connection from " + a + " to " + b + "]"
+ );
+ }
+}
+....
+
+===== Compiling and Running the Basic Simulation
+
+The source files for the basic system are listed in the file
+`basic.lst`. To build and run the basic system, in a shell window, type
+these commands:
+
+[source, text]
+....
+ajc -argfile telecom/basic.lst
+java telecom.BasicSimulation
+....
+
+===== The Timing aspect
+
+The `Timing` aspect keeps track of total connection time for each
+`Customer` by starting and stopping a timer associated with each
+connection. It uses some helper classes:
+
+====== The `Timer` class
+
+A `Timer` object simply records the current time when it is started and
+stopped, and returns their difference when asked for the elapsed time.
+The aspect `TimerLog` (below) can be used to cause the start and stop
+times to be printed to standard output.
+
+[source, java]
+....
+class Timer {
+ long startTime, stopTime;
+
+ public void start() {
+ startTime = System.currentTimeMillis();
+ stopTime = startTime;
+ }
+
+ public void stop() {
+ stopTime = System.currentTimeMillis();
+ }
+
+ public long getTime() {
+ return stopTime - startTime;
+ }
+}
+....
+
+===== The `TimerLog` aspect
+
+The `TimerLog` aspect can be included in a build to get the timer to
+announce when it is started and stopped.
+
+[source, java]
+....
+public aspect TimerLog {
+
+ after(Timer t): target(t) && call(* Timer.start()) {
+ System.err.println("Timer started: " + t.startTime);
+ }
+
+ after(Timer t): target(t) && call(* Timer.stop()) {
+ System.err.println("Timer stopped: " + t.stopTime);
+ }
+}
+....
+
+===== The `Timing` aspect
+
+The `Timing` aspect is declares an inter-type field `totalConnectTime`
+for `Customer` to store the accumulated connection time per `Customer`.
+It also declares that each `Connection` object has a timer.
+
+[source, java]
+....
+public long Customer.totalConnectTime = 0;
+private Timer Connection.timer = new Timer();
+....
+
+Two pieces of after advice ensure that the timer is started when a
+connection is completed and and stopped when it is dropped. The pointcut
+`endTiming` is defined so that it can be used by the `Billing` aspect.
+
+[source, java]
+....
+public aspect Timing {
+
+ public long Customer.totalConnectTime = 0;
+
+ public long getTotalConnectTime(Customer cust) {
+ return cust.totalConnectTime;
+ }
+ private Timer Connection.timer = new Timer();
+ public Timer getTimer(Connection conn) { return conn.timer; }
+
+ after (Connection c): target(c) && call(void Connection.complete()) {
+ getTimer(c).start();
+ }
+
+ pointcut endTiming(Connection c): target(c) &&
+ call(void Connection.drop());
+
+ after(Connection c): endTiming(c) {
+ getTimer(c).stop();
+ c.getCaller().totalConnectTime += getTimer(c).getTime();
+ c.getReceiver().totalConnectTime += getTimer(c).getTime();
+ }
+}
+....
+
+===== The `Billing` aspect
+
+The Billing system adds billing functionality to the telecom application
+on top of timing.
+
+The `Billing` aspect declares that each `Connection` has a `payer`
+inter-type field to indicate who initiated the call and therefore who is
+responsible to pay for it. It also declares the inter-type method
+`callRate` of `Connection` so that local and long distance calls can be
+charged differently. The call charge must be calculated after the timer
+is stopped; the after advice on pointcut `Timing.endTiming` does this,
+and `Billing` is declared to be more precedent than `Timing` to make
+sure that this advice runs after ``Timing``'s advice on the same join
+point. Finally, it declares inter-type methods and fields for `Customer`
+to handle the `totalCharge`.
+
+[source, java]
+....
+public aspect Billing {
+ // precedence required to get advice on endtiming in the right order
+ declare precedence: Billing, Timing;
+
+ public static final long LOCAL_RATE = 3;
+ public static final long LONG_DISTANCE_RATE = 10;
+
+ public Customer Connection.payer;
+ public Customer getPayer(Connection conn) { return conn.payer; }
+
+ after(Customer cust) returning (Connection conn):
+ args(cust, ..) && call(Connection+.new(..)) {
+ conn.payer = cust;
+ }
+
+ public abstract long Connection.callRate();
+
+ public long LongDistance.callRate() { return LONG_DISTANCE_RATE; }
+ public long Local.callRate() { return LOCAL_RATE; }
+
+ after(Connection conn): Timing.endTiming(conn) {
+ long time = Timing.aspectOf().getTimer(conn).getTime();
+ long rate = conn.callRate();
+ long cost = rate * time;
+ getPayer(conn).addCharge(cost);
+ }
+
+ public long Customer.totalCharge = 0;
+ public long getTotalCharge(Customer cust) { return cust.totalCharge; }
+
+ public void Customer.addCharge(long charge) {
+ totalCharge += charge;
+ }
+}
+....
+
+===== Accessing the inter-type state
+
+Both the aspects `Timing` and `Billing` contain the definition of
+operations that the rest of the system may want to access. For example,
+when running the simulation with one or both aspects, we want to find
+out how much time each customer spent on the telephone and how big their
+bill is. That information is also stored in the classes, but they are
+accessed through static methods of the aspects, since the state they
+refer to is private to the aspect.
+
+Take a look at the file `TimingSimulation.java`. The most important
+method of this class is the method `report(Customer)`, which is used in
+the method run of the superclass `AbstractSimulation`. This method is
+intended to print out the status of the customer, with respect to the
+`Timing` feature.
+
+[source, java]
+....
+protected void report(Customer c){
+ Timing t = Timing.aspectOf();
+ System.out.println(c + " spent " + t.getTotalConnectTime(c));
+}
+....
+
+===== Compiling and Running
+
+The files timing.lst and billing.lst contain file lists for the timing
+and billing configurations. To build and run the application with only
+the timing feature, go to the directory examples and type:
+
+[source, text]
+....
+ajc -argfile telecom/timing.lst
+java telecom.TimingSimulation
+....
+
+To build and run the application with the timing and billing features,
+go to the directory examples and type:
+
+[source, text]
+....
+ajc -argfile telecom/billing.lst
+java telecom.BillingSimulation
+....
+
+===== Discussion
+
+There are some explicit dependencies between the aspects `Billing` and
+`Timing`:
+
+* `Billing` is declared more precedent than `Timing` so that ``Billing``'s after
+advice runs after that of `Timing` when they are on the same join point.
+* `Billing` uses the pointcut `Timing.endTiming`.
+* `Billing` needs access to the timer associated with a connection.
+
+[[examples-reusable]]
+=== Reusable Aspects
+
+==== Tracing using Aspects, Revisited
+
+(The code for this example is in `InstallDir/examples/tracing`.)
+
+===== Tracing - Version 3
+
+One advantage of not exposing the methods `traceEntry` and `traceExit` as
+public operations is that we can easily change their interface without
+any dramatic consequences in the rest of the code.
+
+Consider, again, the program without AspectJ. Suppose, for example, that
+at some point later the requirements for tracing change, stating that
+the trace messages should always include the string representation of
+the object whose methods are being traced. This can be achieved in at
+least two ways. One way is keep the interface of the methods
+`traceEntry` and `traceExit` as it was before,
+
+[source, java]
+....
+public static void traceEntry(String str);
+public static void traceExit(String str);
+....
+
+In this case, the caller is responsible for ensuring that the string
+representation of the object is part of the string given as argument.
+So, calls must look like:
+
+[source, java]
+....
+Trace.traceEntry("Square.distance in " + toString());
+....
+
+Another way is to enforce the requirement with a second argument in the
+trace operations, e.g.
+
+[source, java]
+....
+public static void traceEntry(String str, Object obj);
+public static void traceExit(String str, Object obj);
+....
+
+In this case, the caller is still responsible for sending the right
+object, but at least there is some guarantees that some object will be
+passed. The calls will look like:
+
+[source, java]
+....
+Trace.traceEntry("Square.distance", this);
+....
+
+In either case, this change to the requirements of tracing will have
+dramatic consequences in the rest of the code -- every call to the trace
+operations `traceEntry` and `traceExit` must be changed!
+
+Here's another advantage of doing tracing with an aspect. We've already
+seen that in version 2 `traceEntry` and `traceExit` are not publicly
+exposed. So changing their interfaces, or the way they are used, has
+only a small effect inside the `Trace` class. Here's a partial view at
+the implementation of `Trace`, version 3. The differences with respect
+to version 2 are stressed in the comments:
+
+[source, java]
+....
+abstract aspect Trace {
+
+ public static int TRACELEVEL = 0;
+ protected static PrintStream stream = null;
+ protected static int callDepth = 0;
+
+ public static void initStream(PrintStream s) {
+ stream = s;
+ }
+
+ protected static void traceEntry(String str, Object o) {
+ if (TRACELEVEL == 0) return;
+ if (TRACELEVEL == 2) callDepth++;
+ printEntering(str + ": " + o.toString());
+ }
+
+ protected static void traceExit(String str, Object o) {
+ if (TRACELEVEL == 0) return;
+ printExiting(str + ": " + o.toString());
+ if (TRACELEVEL == 2) callDepth--;
+ }
+
+ private static void printEntering(String str) {
+ printIndent();
+ stream.println("Entering " + str);
+ }
+
+ private static void printExiting(String str) {
+ printIndent();
+ stream.println("Exiting " + str);
+ }
+
+ private static void printIndent() {
+ for (int i = 0; i < callDepth; i++)
+ stream.print(" ");
+ }
+
+ abstract pointcut myClass(Object obj);
+
+ pointcut myConstructor(Object obj): myClass(obj) && execution(new(..));
+ pointcut myMethod(Object obj):
+ myClass(obj) && execution(* *(..)) && !execution(String toString());
+
+ before(Object obj): myConstructor(obj) {
+ traceEntry("" + thisJoinPointStaticPart.getSignature(), obj);
+ }
+ after(Object obj): myConstructor(obj) {
+ traceExit("" + thisJoinPointStaticPart.getSignature(), obj);
+ }
+
+ before(Object obj): myMethod(obj) {
+ traceEntry("" + thisJoinPointStaticPart.getSignature(), obj);
+ }
+ after(Object obj): myMethod(obj) {
+ traceExit("" + thisJoinPointStaticPart.getSignature(), obj);
+ }
+}
+....
+
+As you can see, we decided to apply the first design by preserving the
+interface of the methods `traceEntry` and `traceExit`. But it doesn't
+matter - we could as easily have applied the second design (the code in
+the directory `examples/tracing/version3` has the second design). The
+point is that the effects of this change in the tracing requirements are
+limited to the `Trace` aspect class.
+
+One implementation change worth noticing is the specification of the
+pointcuts. They now expose the object. To maintain full consistency with
+the behavior of version 2, we should have included tracing for static
+methods, by defining another pointcut for static methods and advising
+it. We leave that as an exercise.
+
+Moreover, we had to exclude the execution join point of the method
+`toString` from the `methods` pointcut. The problem here is that
+`toString` is being called from inside the advice. Therefore if we trace
+it, we will end up in an infinite recursion of calls. This is a subtle
+point, and one that you must be aware when writing advice. If the advice
+calls back to the objects, there is always the possibility of recursion.
+Keep that in mind!
+
+In fact, esimply excluding the execution join point may not be enough,
+if there are calls to other traced methods within it - in which case,
+the restriction should be
+
+[source, java]
+....
+&& !cflow(execution(String toString()))
+....
+
+excluding both the execution of `toString` methods and all join points
+under that execution.
+
+In summary, to implement the change in the tracing requirements we had
+to make a couple of changes in the implementation of the `Trace` aspect
+class, including changing the specification of the pointcuts. That's
+only natural. But the implementation changes were limited to this
+aspect. Without aspects, we would have to change the implementation of
+every application class.
+
+Finally, to run this version of tracing, go to the directory `examples`
+and type:
+
+[source, text]
+....
+ajc -argfile tracing/tracev3.lst
+....
+
+The file `tracev3.lst` lists the application classes as well as this
+version of the files `Trace.java` and `TraceMyClasses.java`. To run the
+program, type
+
+[source, text]
+....
+java tracing.version3.TraceMyClasses
+....
+
+The output should be:
+
+[source, text]
+....
+ --> tracing.TwoDShape(double, double)
+ <-- tracing.TwoDShape(double, double)
+ --> tracing.Circle(double, double, double)
+ <-- tracing.Circle(double, double, double)
+ --> tracing.TwoDShape(double, double)
+ <-- tracing.TwoDShape(double, double)
+ --> tracing.Circle(double, double, double)
+ <-- tracing.Circle(double, double, double)
+ --> tracing.Circle(double)
+ <-- tracing.Circle(double)
+ --> tracing.TwoDShape(double, double)
+ <-- tracing.TwoDShape(double, double)
+ --> tracing.Square(double, double, double)
+ <-- tracing.Square(double, double, double)
+ --> tracing.Square(double, double)
+ <-- tracing.Square(double, double)
+ --> double tracing.Circle.perimeter()
+ <-- double tracing.Circle.perimeter()
+c1.perimeter() = 12.566370614359172
+ --> double tracing.Circle.area()
+ <-- double tracing.Circle.area()
+c1.area() = 12.566370614359172
+ --> double tracing.Square.perimeter()
+ <-- double tracing.Square.perimeter()
+s1.perimeter() = 4.0
+ --> double tracing.Square.area()
+ <-- double tracing.Square.area()
+s1.area() = 1.0
+ --> double tracing.TwoDShape.distance(TwoDShape)
+ --> double tracing.TwoDShape.getX()
+ <-- double tracing.TwoDShape.getX()
+ --> double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.distance(TwoDShape)
+c2.distance(c1) = 4.242640687119285
+ --> double tracing.TwoDShape.distance(TwoDShape)
+ --> double tracing.TwoDShape.getX()
+ <-- double tracing.TwoDShape.getX()
+ --> double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.getY()
+ <-- double tracing.TwoDShape.distance(TwoDShape)
+s1.distance(c1) = 2.23606797749979
+ --> String tracing.Square.toString()
+ --> String tracing.TwoDShape.toString()
+ <-- String tracing.TwoDShape.toString()
+ <-- String tracing.Square.toString()
+s1.toString(): Square side = 1.0 @ (1.0, 2.0)
+....