aboutsummaryrefslogtreecommitdiffstats
path: root/docs/progGuideDB/examples.adoc
diff options
context:
space:
mode:
Diffstat (limited to 'docs/progGuideDB/examples.adoc')
-rw-r--r--docs/progGuideDB/examples.adoc1918
1 files changed, 0 insertions, 1918 deletions
diff --git a/docs/progGuideDB/examples.adoc b/docs/progGuideDB/examples.adoc
deleted file mode 100644
index f01ae46d8..000000000
--- a/docs/progGuideDB/examples.adoc
+++ /dev/null
@@ -1,1918 +0,0 @@
-[[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)
-....