From: wisberg Date: Mon, 10 Mar 2003 09:34:37 +0000 (+0000) Subject: manual test for experimental ant task option to copy non-.class file contents of... X-Git-Tag: v1_1_0_RC1~44 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1c3c02518fc13f022230acac07db10649776955b;p=aspectj.git manual test for experimental ant task option to copy non-.class file contents of input jars to the output jar after each compile or recompile. Currently showing VerifyError on recompile -- need to investigate further. --- diff --git a/taskdefs/testdata/incTest/incTest.xml b/taskdefs/testdata/incTest/incTest.xml new file mode 100644 index 000000000..615cac04d --- /dev/null +++ b/taskdefs/testdata/incTest/incTest.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/taskdefs/testdata/incTest/injarSrc/one/InjarOneMain.java b/taskdefs/testdata/incTest/injarSrc/one/InjarOneMain.java new file mode 100644 index 000000000..0eba13066 --- /dev/null +++ b/taskdefs/testdata/incTest/injarSrc/one/InjarOneMain.java @@ -0,0 +1,6 @@ + +public class InjarOneMain { + public static void main(String[] args) { + System.out.println("in InjarOneMain.main(..)"); + } +} diff --git a/taskdefs/testdata/incTest/injarSrc/one/one.properties b/taskdefs/testdata/incTest/injarSrc/one/one.properties new file mode 100644 index 000000000..2aaac9a85 --- /dev/null +++ b/taskdefs/testdata/incTest/injarSrc/one/one.properties @@ -0,0 +1 @@ +hel=lo diff --git a/taskdefs/testdata/incTest/injarSrc/one/overview.gif b/taskdefs/testdata/incTest/injarSrc/one/overview.gif new file mode 100644 index 000000000..7b1d6c8d6 Binary files /dev/null and b/taskdefs/testdata/incTest/injarSrc/one/overview.gif differ diff --git a/taskdefs/testdata/incTest/injarSrc/one/subdir/examples.xml b/taskdefs/testdata/incTest/injarSrc/one/subdir/examples.xml new file mode 100644 index 000000000..2e6a1715d --- /dev/null +++ b/taskdefs/testdata/incTest/injarSrc/one/subdir/examples.xml @@ -0,0 +1,2343 @@ + + Examples + + + About this Chapter + + 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. + + + + + + + Obtaining, Compiling and Running the Examples + + The examples source code is part of AspectJ's documentation + distribution which may be downloaded from the AspectJ download page. + + Compiling most examples should be 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 + + +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: + + +(In Unix use a : in the CLASSPATH) +java -classpath ".:InstallDir/lib/aspectjrt.jar" telecom.billingSimulation + + + +(In Windows use a ; in the CLASSPATH) +java -classpath ".;InstallDir/lib/aspectjrt.jar" telecom.billingSimulation + + + + + + + + + + + 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, , is about gathering and using + information about the join point that has triggered some advice. The + second example, , concerns changing an existing class + hierarchy. + + + + + Join Points and <literal>thisJoinPoint</literal> + 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 + join point is reached, before, after or around advice on that join + point may be run. + + When dealing with pointcuts that pick out join points of specific + method calls, field gets, or the like, the advice will know exactly what + kind of join point it is executing under. It might even have access to + context given by its pointcut. Here, for example, since the only join + points reached will be calls of a certain method, we can get the target + and one of the args of the method directly. + + + + + But sometimes the join point is not so clear. For + instance, suppose a complex application is being debugged, and one + would like to know when any method in some class is being executed. + Then, the pointcut + + + + will select all join points where a method defined within the class + ProblemClass is being executed. But advice + executes when a particular join point is matched, and so the question, + "Which join point was matched?" naturally arises. + + Information about the join point that was matched is available to + advice through the special variable thisJoinPoint, + of type org.aspectj.lang.JoinPoint. This + class provides methods that return + + + the kind of join point that was matched + + the source location of the current join point + + normal, short and long string representations of the + current join point + the actual argument(s) to the method or field selected + by the current join point + the signature of the method or field selected by the + current join point + the target object + the currently executing object + a reference to the static portion of the object + representing the current join point. This is also available through + the special variable thisJoinPointStaticPart. + + + + + The <classname>Demo</classname> 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. + + + + + + + The Aspect <literal>GetInfo</literal> + + 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. + + + Defining the scope of a pointcut + + The pointcut goCut is defined as + 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 Signature, + found using the method getSignature of 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 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. + + + + + + Roles and Views Using Introduction + Roles and Views Using + Introduction + + (The code for this example is in + InstallDir/examples/introduction.) + + Like advice, pieces of introduction are members of an aspect. They + define new members that act as if they were defined on another + class. Unlike advice, introduction affects not only the behavior of the + application, but also the structural relationship between an + application's classes. + + This is crucial: Affecting the class structure of an application at + makes these modifications available to other components of the + application. + + Introduction modifies a class by adding or changing + + member fields + member methods + nested classes + + + and by making the class + + + implement interfaces + extend classes + + + + This example provides three illustrations of the use of introduction to + encapsulate roles or views of a class. The class we will be introducing + into, Point, is a simple class with rectangular + and polar coordinates. Our introduction will make the class + Point, in turn, cloneable, hashable, and + comparable. These facilities are provided by introduction forms without + having to modify the class Point. + + + + The class <classname>Point</classname> + + The class Point 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. + + + + + + + + + + + + + + Making <classname>Point</classname>s Cloneable — The Aspect + <classname>CloneablePoint</classname> + + This first example demonstrates the introduction of a interface + (Cloneable) and a method + (clone) into the class + Point. 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 we have to + override Object.clone with a new method that does + what we want. + + The CloneablePoint aspect uses the + declare parents form to introduce the interface + Cloneable into the class + Point. It then defines a method, + Point.clone, which overrides the method + clone that was inherited from + Object. Point.clone + updates the Point's coordinate systems before + invoking its superclass' clone method. + + + + Note that since aspects define types just as classes define + types, we can define a main method that is + invocable from the command line to use as a test method. + + + + Making <classname>Point</classname>s Comparable — The + Aspect <classname>ComparablePoint</classname> + + This second example introduces another interface and + method into the class Point. + + 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. + + The aspect ComparablePoint introduces + implements Comparable into + Point along with a + compareTo method that can be used to compare + Points. A Point + p1 is said to be less than + another Point p2 if + p1 is closer to the origin. + + + + + + Making <classname>Point</classname>s Hashable — The Aspect + <classname>HashablePoint</classname> + + The third aspect overrides two previously defined methods to + give to Point the hashing behavior we + want. + + The method Object.hashCode returns an unique + integer, suitable for use as a hash table key. Different + implementations are allowed return different integers, but must + return distinct integers for distinct objects, and the same integer + for objects that test equal. But since the default implementation + of Object.equal 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. + + The class HashablePoint introduces the + methods hashCode and equals + into the class Point. These methods use + 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. + + + + Again, we supply a main method in the aspect + for testing. + + + + + + + + + + + + + Development Aspects + + + Tracing Aspects + + (The code for this example is in + InstallDir/examples/tracing.) + + + + Overview + + + 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 so. + + + + 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: + + + + + + TwoDShape has two subclasses, + Circle and Square: + + + + + + + + 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: + + + +ajc -argfile tracing/notrace.lst + + + To run the program, type + + +java tracing.ExampleMain + + + (we don't need anything special on the classpath since this is pure + Java code). You should see the following output: + + + + + + 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: + + + + + + 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): + + + + + + 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: + + + + + + Running the main method of + tracing.version1.TraceMyClasses should produce + the output: + + + 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 + + + + + + In order to use it, we need to define our own subclass that knows about + our application classes, in version2/TraceMyClasses.java: + + + + + + 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: + + + + + + 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: + + + " + 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. + + + + + + + + + + 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 a Java beans + with bound properties. + + + Introduction + + 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 + getproperty 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 setproperty 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 Class <classname>Point</classname> + + + The class Point is a very simple class with + trivial getters and setters, and a simple vector offset method. + + + + + + + + The Aspect <classname>BoundPoint</classname> + + + The aspect BoundPoint adds "beanness" to + Point objects. The first thing it does is + privately introduce a reference to an instance of + PropertyChangeSupport into all + Point objects. 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. The support field is + privately introduced, so only the code in the aspect can refer to it. + + + + Methods for registering and managing listeners for property change + events are introduced into Point by the + introductions. These methods delegate the work to the + property change support object. + + + + The introduction also makes Point implement the + Serializable interface. Implementing + Serializable does not require any methods to be + implemented. Serialization for Point objects is + provided by the default serialization method. + + + + The pointcut setters names the + set methods: reception by a + Point object of 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. Note that the call to the method proceed needs to pass along + the Point p. The rule of thumb is that context that + an around advice exposes must be passed forward to continue. + + + + + + + + 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. + + + + + + + Compiling and Running the Example + To compile and run this example, go to the examples directory and type: + + + + + + + + + + + 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. + + + Overview + + 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: + + + + + 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. + + + + + + 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. + + + + + + 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 introduces state and + behavior onto the Subject and + Observer interfaces. + + + + + 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. + + + + + + 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. + + + + + + Finally, the SubjectObserverProtocolImpl + implements the subject/observer protocol, with + Button objects as subjects and + ColorLabel objects as observers: + + + + + + It does this by introducing the appropriate interfaces onto the + Button and ColorLabel + classes, making sure the methods required by the interfaces are + implemented, and providing a definition for the + 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: + + + + + + + + + + + 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. + + + + + + + + + + The Class <classname>Customer</classname> + + + Customer has methods call, + pickup, hangup and + merge for managing calls. + + + + + + + + The Class <classname>Call</classname> + + + 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 Class <classname>Connection</classname> + + 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. + + + + + + + + The Class Local + + + + + + + The Class LongDistance + + + + + + + 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: + + + + + + + + Timing + + 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 Class <classname>Timer</classname> + + + 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. + + + + + + + + + The Aspect <classname>TimerLog</classname> + + + The aspect TimerLog can be included in a + build to get the timer to announce when it is started and stopped. + + + + + + + + The Aspect <classname>Timing</classname> + + + The aspect Timing introduces attribute + totalConnectTime into the class + Customer to store the accumulated connection + time per Customer. It introduces attribute + timer into Connection to associate a timer + with each Connection. 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. + + + + + + + + Billing + + + The Billing system adds billing functionality to the telecom + application on top of timing. + + + + The Aspect <classname>Billing</classname> + + + The aspect Billing introduces attribute + payer into Connection + to indicate who initiated the call and therefore who is + responsible to pay for it. It also introduces method + callRate into 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 dominates Timing to make + sure that this advice runs after Timing's + advice on the same join point. It introduces attribute + totalCharge and its associated methods into + Customer (to manage the + customer's bill information. + + + + + + + + + Accessing the Introduced 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 c), 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. + + + + + + + + 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: + + + + + + To build and run the application with the timing and billing features, + go to the directory examples and type: + + + + + + + + Discussion + + + There are some explicit dependencies between the aspects Billing and + Timing: + + + + Billing is declared to dominate 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. + + + + + + + + + + + + + Reusable Aspects + + + Tracing 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, + + + + + + 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: + + + + + + Another way is to enforce the requirement with a second argument in the + trace operations, e.g. + + + + + + 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: + + + + + + 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: + + + + + + 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 + + + + + + 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: + + + + + + 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 + + + + + The output should be: + + 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) +]]> + + + + + + + diff --git a/taskdefs/testdata/incTest/injarSrc/two/InjarTwoMain.java b/taskdefs/testdata/incTest/injarSrc/two/InjarTwoMain.java new file mode 100644 index 000000000..c730f31e6 --- /dev/null +++ b/taskdefs/testdata/incTest/injarSrc/two/InjarTwoMain.java @@ -0,0 +1,6 @@ + +public class InjarTwoMain { + public static void main(String[] args) { + System.out.println("in InjarTwoMain.main(..)"); + } +} diff --git a/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/overview2.gif b/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/overview2.gif new file mode 100644 index 000000000..7b1d6c8d6 Binary files /dev/null and b/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/overview2.gif differ diff --git a/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/subdir/twoexamples.xml b/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/subdir/twoexamples.xml new file mode 100644 index 000000000..2e6a1715d --- /dev/null +++ b/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/subdir/twoexamples.xml @@ -0,0 +1,2343 @@ + + Examples + + + About this Chapter + + 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. + + + + + + + Obtaining, Compiling and Running the Examples + + The examples source code is part of AspectJ's documentation + distribution which may be downloaded from the AspectJ download page. + + Compiling most examples should be 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 + + +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: + + +(In Unix use a : in the CLASSPATH) +java -classpath ".:InstallDir/lib/aspectjrt.jar" telecom.billingSimulation + + + +(In Windows use a ; in the CLASSPATH) +java -classpath ".;InstallDir/lib/aspectjrt.jar" telecom.billingSimulation + + + + + + + + + + + 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, , is about gathering and using + information about the join point that has triggered some advice. The + second example, , concerns changing an existing class + hierarchy. + + + + + Join Points and <literal>thisJoinPoint</literal> + 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 + join point is reached, before, after or around advice on that join + point may be run. + + When dealing with pointcuts that pick out join points of specific + method calls, field gets, or the like, the advice will know exactly what + kind of join point it is executing under. It might even have access to + context given by its pointcut. Here, for example, since the only join + points reached will be calls of a certain method, we can get the target + and one of the args of the method directly. + + + + + But sometimes the join point is not so clear. For + instance, suppose a complex application is being debugged, and one + would like to know when any method in some class is being executed. + Then, the pointcut + + + + will select all join points where a method defined within the class + ProblemClass is being executed. But advice + executes when a particular join point is matched, and so the question, + "Which join point was matched?" naturally arises. + + Information about the join point that was matched is available to + advice through the special variable thisJoinPoint, + of type org.aspectj.lang.JoinPoint. This + class provides methods that return + + + the kind of join point that was matched + + the source location of the current join point + + normal, short and long string representations of the + current join point + the actual argument(s) to the method or field selected + by the current join point + the signature of the method or field selected by the + current join point + the target object + the currently executing object + a reference to the static portion of the object + representing the current join point. This is also available through + the special variable thisJoinPointStaticPart. + + + + + The <classname>Demo</classname> 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. + + + + + + + The Aspect <literal>GetInfo</literal> + + 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. + + + Defining the scope of a pointcut + + The pointcut goCut is defined as + 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 Signature, + found using the method getSignature of 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 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. + + + + + + Roles and Views Using Introduction + Roles and Views Using + Introduction + + (The code for this example is in + InstallDir/examples/introduction.) + + Like advice, pieces of introduction are members of an aspect. They + define new members that act as if they were defined on another + class. Unlike advice, introduction affects not only the behavior of the + application, but also the structural relationship between an + application's classes. + + This is crucial: Affecting the class structure of an application at + makes these modifications available to other components of the + application. + + Introduction modifies a class by adding or changing + + member fields + member methods + nested classes + + + and by making the class + + + implement interfaces + extend classes + + + + This example provides three illustrations of the use of introduction to + encapsulate roles or views of a class. The class we will be introducing + into, Point, is a simple class with rectangular + and polar coordinates. Our introduction will make the class + Point, in turn, cloneable, hashable, and + comparable. These facilities are provided by introduction forms without + having to modify the class Point. + + + + The class <classname>Point</classname> + + The class Point 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. + + + + + + + + + + + + + + Making <classname>Point</classname>s Cloneable — The Aspect + <classname>CloneablePoint</classname> + + This first example demonstrates the introduction of a interface + (Cloneable) and a method + (clone) into the class + Point. 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 we have to + override Object.clone with a new method that does + what we want. + + The CloneablePoint aspect uses the + declare parents form to introduce the interface + Cloneable into the class + Point. It then defines a method, + Point.clone, which overrides the method + clone that was inherited from + Object. Point.clone + updates the Point's coordinate systems before + invoking its superclass' clone method. + + + + Note that since aspects define types just as classes define + types, we can define a main method that is + invocable from the command line to use as a test method. + + + + Making <classname>Point</classname>s Comparable — The + Aspect <classname>ComparablePoint</classname> + + This second example introduces another interface and + method into the class Point. + + 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. + + The aspect ComparablePoint introduces + implements Comparable into + Point along with a + compareTo method that can be used to compare + Points. A Point + p1 is said to be less than + another Point p2 if + p1 is closer to the origin. + + + + + + Making <classname>Point</classname>s Hashable — The Aspect + <classname>HashablePoint</classname> + + The third aspect overrides two previously defined methods to + give to Point the hashing behavior we + want. + + The method Object.hashCode returns an unique + integer, suitable for use as a hash table key. Different + implementations are allowed return different integers, but must + return distinct integers for distinct objects, and the same integer + for objects that test equal. But since the default implementation + of Object.equal 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. + + The class HashablePoint introduces the + methods hashCode and equals + into the class Point. These methods use + 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. + + + + Again, we supply a main method in the aspect + for testing. + + + + + + + + + + + + + Development Aspects + + + Tracing Aspects + + (The code for this example is in + InstallDir/examples/tracing.) + + + + Overview + + + 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 so. + + + + 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: + + + + + + TwoDShape has two subclasses, + Circle and Square: + + + + + + + + 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: + + + +ajc -argfile tracing/notrace.lst + + + To run the program, type + + +java tracing.ExampleMain + + + (we don't need anything special on the classpath since this is pure + Java code). You should see the following output: + + + + + + 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: + + + + + + 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): + + + + + + 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: + + + + + + Running the main method of + tracing.version1.TraceMyClasses should produce + the output: + + + 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 + + + + + + In order to use it, we need to define our own subclass that knows about + our application classes, in version2/TraceMyClasses.java: + + + + + + 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: + + + + + + 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: + + + " + 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. + + + + + + + + + + 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 a Java beans + with bound properties. + + + Introduction + + 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 + getproperty 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 setproperty 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 Class <classname>Point</classname> + + + The class Point is a very simple class with + trivial getters and setters, and a simple vector offset method. + + + + + + + + The Aspect <classname>BoundPoint</classname> + + + The aspect BoundPoint adds "beanness" to + Point objects. The first thing it does is + privately introduce a reference to an instance of + PropertyChangeSupport into all + Point objects. 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. The support field is + privately introduced, so only the code in the aspect can refer to it. + + + + Methods for registering and managing listeners for property change + events are introduced into Point by the + introductions. These methods delegate the work to the + property change support object. + + + + The introduction also makes Point implement the + Serializable interface. Implementing + Serializable does not require any methods to be + implemented. Serialization for Point objects is + provided by the default serialization method. + + + + The pointcut setters names the + set methods: reception by a + Point object of 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. Note that the call to the method proceed needs to pass along + the Point p. The rule of thumb is that context that + an around advice exposes must be passed forward to continue. + + + + + + + + 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. + + + + + + + Compiling and Running the Example + To compile and run this example, go to the examples directory and type: + + + + + + + + + + + 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. + + + Overview + + 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: + + + + + 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. + + + + + + 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. + + + + + + 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 introduces state and + behavior onto the Subject and + Observer interfaces. + + + + + 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. + + + + + + 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. + + + + + + Finally, the SubjectObserverProtocolImpl + implements the subject/observer protocol, with + Button objects as subjects and + ColorLabel objects as observers: + + + + + + It does this by introducing the appropriate interfaces onto the + Button and ColorLabel + classes, making sure the methods required by the interfaces are + implemented, and providing a definition for the + 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: + + + + + + + + + + + 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. + + + + + + + + + + The Class <classname>Customer</classname> + + + Customer has methods call, + pickup, hangup and + merge for managing calls. + + + + + + + + The Class <classname>Call</classname> + + + 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 Class <classname>Connection</classname> + + 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. + + + + + + + + The Class Local + + + + + + + The Class LongDistance + + + + + + + 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: + + + + + + + + Timing + + 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 Class <classname>Timer</classname> + + + 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. + + + + + + + + + The Aspect <classname>TimerLog</classname> + + + The aspect TimerLog can be included in a + build to get the timer to announce when it is started and stopped. + + + + + + + + The Aspect <classname>Timing</classname> + + + The aspect Timing introduces attribute + totalConnectTime into the class + Customer to store the accumulated connection + time per Customer. It introduces attribute + timer into Connection to associate a timer + with each Connection. 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. + + + + + + + + Billing + + + The Billing system adds billing functionality to the telecom + application on top of timing. + + + + The Aspect <classname>Billing</classname> + + + The aspect Billing introduces attribute + payer into Connection + to indicate who initiated the call and therefore who is + responsible to pay for it. It also introduces method + callRate into 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 dominates Timing to make + sure that this advice runs after Timing's + advice on the same join point. It introduces attribute + totalCharge and its associated methods into + Customer (to manage the + customer's bill information. + + + + + + + + + Accessing the Introduced 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 c), 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. + + + + + + + + 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: + + + + + + To build and run the application with the timing and billing features, + go to the directory examples and type: + + + + + + + + Discussion + + + There are some explicit dependencies between the aspects Billing and + Timing: + + + + Billing is declared to dominate 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. + + + + + + + + + + + + + Reusable Aspects + + + Tracing 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, + + + + + + 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: + + + + + + Another way is to enforce the requirement with a second argument in the + trace operations, e.g. + + + + + + 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: + + + + + + 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: + + + + + + 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 + + + + + + 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: + + + + + + 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 + + + + + The output should be: + + 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) +]]> + + + + + + + diff --git a/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/two.properties b/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/two.properties new file mode 100644 index 000000000..2aaac9a85 --- /dev/null +++ b/taskdefs/testdata/incTest/injarSrc/two/twoSubdir/two.properties @@ -0,0 +1 @@ +hel=lo diff --git a/taskdefs/testdata/incTest/readme-incTest.html b/taskdefs/testdata/incTest/readme-incTest.html new file mode 100644 index 000000000..70dbd9a37 --- /dev/null +++ b/taskdefs/testdata/incTest/readme-incTest.html @@ -0,0 +1,97 @@ + +Incremental test of AjcTask Ant task + +

Incremental test of AjcTask Ant task

+This directory contains files for manually testing the +incremental behavior of the AjcTask Ant task. +It has an Ant script which builds two input jars +and runs an incremental test using a tag file. +

+The results must be verified by hand manually at present; +This test should be updated to run automatically. + +

Sample procedure

+This shows how to set up the test and check whether +the incremental tag file and injar-copying features work. +Throughout, {ant} is assumed to be +../../../lib/ant/bin/ant and the directory +is assumed to be this directory, with the Ant build script +incTest.xml. +The script uses the eclipse classpath rather than aspectjtools.jar, +so that updates to the sources can be tested without +rebuilding the distribution. + +
    +
  1. Set up the output jars +
      {ant} -f incTest.xml setup
    + This should produce one.jar and two.jar + in the directory injars/. +

    +

  2. + +
  3. Start the run with the default settings + (use output/tagfile.txt as the tag file + and to include the non-.class contents of the input jars + in the output jar): +
      {ant} -f incTest.xml test
    +The task will do an initial compilation and silently wait. +

    +

  4. +
  5. In another shell, test the classes in the generated output jar: +
      export CLASSPATH="../../../lib/test/aspectjrt.jar;output/outjar.jar"
    +  java packageOne.Main
    + Inspect the files in the src/ and + injarSrc/ directories to find other + main classes and to determine the expected result of the aspect. + As of this writing, + the aspect src/TraceMains.java + should emit messages before and after + the execution of any static main(String[]) method. +

    +

  6. +
  7. Also test that the injar files were copied to the output jar. + The non-.class files in injarSrc/ + should be included in the output jar: +
      jar tf output/outjar.jar
    +

    +

  8. +
  9. To test incremental compiles, + edit the aspect src/TraceMains.java + to modify the behavior. You can also modify the input jars or + source files. + Then touch or edit output/tagfile.txt + to provoke recompilation. +

    +

  10. +
  11. Test the generated output jar again + to see if the changes are reflected in the runtime behavior + and if the non-.class files are being copied correctly. +

    +

  12. +
  13. Repeat this process until satisfied. Delete the tagfile to quit: +
      rm output/tagfile.txt
    +

    +

  14. +
+ +To test the -incremental option or to test without +copying input jar files or using an output jar, edit the +incTest.xml script and redo the test. +

+ +

Clean-up

+Take care to preserve CVS state after running this test. +It calls on you to edit checked-in files and generates files +to local repository directories. The best thing is to delete +the output and injars directories +and revert to repository forms for sources (unless fixing them). + +

Upgrade

+This needs to be upgraded to run manually. +There is a file/tree-comparison utility to revive from the +aspectj-attic which can be used to compare expected and actual +results of the jar files. Setting up the tasks to run incrementally +might be done using the parallel task, but... +
+ + diff --git a/taskdefs/testdata/incTest/src/TraceMains.java b/taskdefs/testdata/incTest/src/TraceMains.java new file mode 100644 index 000000000..acf94be57 --- /dev/null +++ b/taskdefs/testdata/incTest/src/TraceMains.java @@ -0,0 +1,14 @@ + +public aspect TraceMains { + + + before() : execution(static void main(String[])) { + String m = thisJoinPointStaticPart.getSignature().toString(); + System.out.println("before " + m); + } + after() returning: execution(static void main(String[])) { + String m = thisJoinPointStaticPart.getSignature().toString(); + System.out.println("after " + m); + } + +} \ No newline at end of file diff --git a/taskdefs/testdata/incTest/src/packageOne/Main.java b/taskdefs/testdata/incTest/src/packageOne/Main.java new file mode 100644 index 000000000..278614543 --- /dev/null +++ b/taskdefs/testdata/incTest/src/packageOne/Main.java @@ -0,0 +1,8 @@ + +package packageOne; + +public class Main { + public static void main(String[] args) { + System.out.println("in packageOne.Main.main(..)"); + } +} \ No newline at end of file