diff options
author | Alexander Kriegisch <Alexander@Kriegisch.name> | 2021-07-16 10:48:06 +0700 |
---|---|---|
committer | Alexander Kriegisch <Alexander@Kriegisch.name> | 2024-01-06 10:09:11 +0100 |
commit | a6a1dbea46fd4829189b23fb900da6a586a8151a (patch) | |
tree | b6d8a3b4e38e320813566535c6ea4f036fb4ba91 /docs/progGuideDB | |
parent | fa63feda31a6a9656173a63dc057993d98469305 (diff) | |
download | aspectj-a6a1dbea46fd4829189b23fb900da6a586a8151a.tar.gz aspectj-a6a1dbea46fd4829189b23fb900da6a586a8151a.zip |
Fix more AsciiDoc links and code blocks (WIP)
- Add Java syntax highlighting to AspectJ and Java files
- Add XML syntax highlighting to XML files (Ant, LTW etc.)
- Dedent and remove empty lines, where necessary
- Enclose in-line line numbers for Java code in /*23*/ comments in order
to enable Java formatting
Signed-off-by: Alexander Kriegisch <Alexander@Kriegisch.name>
Diffstat (limited to 'docs/progGuideDB')
-rw-r--r-- | docs/progGuideDB/examples.adoc | 131 | ||||
-rw-r--r-- | docs/progGuideDB/gettingstarted.adoc | 42 | ||||
-rw-r--r-- | docs/progGuideDB/idioms.adoc | 5 | ||||
-rw-r--r-- | docs/progGuideDB/implementation.adoc | 6 | ||||
-rw-r--r-- | docs/progGuideDB/language.adoc | 115 | ||||
-rw-r--r-- | docs/progGuideDB/pitfalls.adoc | 5 | ||||
-rw-r--r-- | docs/progGuideDB/quickreference.adoc | 314 | ||||
-rw-r--r-- | docs/progGuideDB/semantics.adoc | 310 |
8 files changed, 402 insertions, 526 deletions
diff --git a/docs/progGuideDB/examples.adoc b/docs/progGuideDB/examples.adoc index fccd57eff..5b20a1b4e 100644 --- a/docs/progGuideDB/examples.adoc +++ b/docs/progGuideDB/examples.adoc @@ -7,35 +7,27 @@ 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. +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 AspectJ project page ( -http://eclipse.org/aspectj[] ). +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 .... @@ -45,11 +37,13 @@ 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 @@ -64,7 +58,7 @@ 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 ], is about +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. @@ -87,6 +81,7 @@ 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) @@ -101,6 +96,7 @@ 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(* *(..)); @@ -124,19 +120,16 @@ 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 -+ -. +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 +with suitable arguments, by ``Demo``'s `go` method which was invoked from within its `main` method. +[source, java] .... public class Demo { static Demo d; @@ -168,6 +161,7 @@ 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 { @@ -207,6 +201,7 @@ aspect GetInfo { The pointcut `goCut` is defined as +[source, java] .... cflow(this(Demo)) && execution(void go()) .... @@ -221,7 +216,7 @@ advised. 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] +xref:../api/org/aspectj/lang/Signature.html[`org.aspectj.lang.Signature`] object returned by calling `getSignature()` on either `thisJoinPoint` or `thisJoinPointStaticPart`. @@ -274,7 +269,7 @@ are provided by AspectJ without having to modify the code for the 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 +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 @@ -292,20 +287,21 @@ image:aspects.gif[image] ===== The `CloneablePoint` aspect -This first aspect is responsible for `Point`'s implementation of the +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 +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 +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 { @@ -338,7 +334,7 @@ public aspect CloneablePoint { ===== The `ComparablePoint` aspect -`ComparablePoint` is responsible for `Point`'s implementation of the +`ComparablePoint` is responsible for ``Point``'s implementation of the `Comparable` interface. The interface `Comparable` defines the single method `compareTo` which @@ -349,11 +345,11 @@ class that implement it. 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. +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 { @@ -391,8 +387,8 @@ public aspect ComparablePoint { ===== 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. +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 @@ -408,14 +404,15 @@ 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 +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 { @@ -489,6 +486,7 @@ 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; @@ -512,6 +510,7 @@ public abstract class TwoDShape { `TwoDShape` has two subclasses, `Circle` and `Square`: +[source, java] .... public class Circle extends TwoDShape { protected double r; @@ -533,6 +532,7 @@ public class Circle extends TwoDShape { } .... +[source, java] .... public class Square extends TwoDShape { protected double s; // side @@ -558,12 +558,14 @@ 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 .... @@ -571,6 +573,7 @@ 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 @@ -588,6 +591,7 @@ 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; @@ -605,8 +609,9 @@ 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] .... -aspect TraceMyClasses { +public aspect TraceMyClasses { pointcut myClass(): within(TwoDShape) || within(Circle) || within(Square); pointcut myConstructor(): myClass() && execution(new(..)); pointcut myMethod(): myClass() && execution(* *(..)); @@ -638,6 +643,7 @@ 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 .... @@ -645,6 +651,7 @@ 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) @@ -707,6 +714,7 @@ 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 { @@ -721,6 +729,7 @@ abstract aspect Trace { 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); @@ -737,6 +746,7 @@ 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 .... @@ -748,6 +758,7 @@ the same trace information as that from version 1. The entire implementation of the new `Trace` class is: +[source, java] .... abstract aspect Trace { @@ -869,6 +880,7 @@ bound property protocol. The `Point` class is a very simple class with trivial getters and setters, and a simple vector offset method. +[source, java] .... class Point { @@ -908,11 +920,12 @@ class Point { ===== The `BoundPoint` aspect -The `BoundPoint` aspect is responsible for `Point`'s "beanness". The +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); .... @@ -923,10 +936,11 @@ 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 +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); @@ -951,6 +965,7 @@ public void Point.hasListeners(String propertyName) { The aspect is also responsible for making sure `Point` implements the `Serializable` interface: +[source, java] .... declare parents: Point implements Serializable; .... @@ -959,13 +974,14 @@ 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: +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); @@ -1025,6 +1041,7 @@ 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 { @@ -1059,6 +1076,7 @@ class Demo implements PropertyChangeListener { To compile and run this example, go to the examples directory and type: +[source, text] .... ajc -argfile bean/files.lst java bean.Demo @@ -1093,6 +1111,7 @@ The generic parts of the protocol are the interfaces `Subject` and view `Observer` objects, and a method for getting data about state changes: +[source, java] .... interface Subject { void addObserver(Observer obs); @@ -1105,6 +1124,7 @@ interface Subject { 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); @@ -1117,6 +1137,7 @@ 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 { @@ -1157,6 +1178,7 @@ its `Subject`. `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 { @@ -1187,6 +1209,7 @@ 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 { @@ -1212,6 +1235,7 @@ Finally, the `SubjectObserverProtocolImpl` implements the subject/observer protocol, with `Button` objects as subjects and `ColorLabel` objects as observers: +[source, java] .... package observer; @@ -1246,6 +1270,7 @@ all `ColorLabel` objects observing that button will `colorCycle`. 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 @@ -1303,6 +1328,7 @@ involved in many calls at one time. image:telecom.gif[image] `Customer` has methods `call`, `pickup`, `hangup` and `merge` for managing calls. +[source, java] .... public class Customer { @@ -1377,6 +1403,7 @@ connection between customers. It does this with a simple state machine connections can be observed. Connection is an abstract class with two concrete subclasses: `Local` and `LongDistance`. +[source, java] .... abstract class Connection { @@ -1422,6 +1449,7 @@ abstract class Connection { 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) { @@ -1432,6 +1460,7 @@ class Local extends Connection { } .... +[source, java] .... class LongDistance extends Connection { LongDistance(Customer a, Customer b) { @@ -1448,6 +1477,7 @@ 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 @@ -1466,6 +1496,7 @@ 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; @@ -1490,6 +1521,7 @@ class Timer { 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 { @@ -1509,6 +1541,7 @@ 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(); @@ -1518,6 +1551,7 @@ 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 { @@ -1556,10 +1590,11 @@ responsible to pay for it. It also declares the inter-type method 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 +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 @@ -1613,6 +1648,7 @@ 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(); @@ -1626,6 +1662,7 @@ 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 @@ -1634,6 +1671,7 @@ 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 @@ -1669,6 +1707,7 @@ 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); @@ -1678,6 +1717,7 @@ 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()); .... @@ -1685,6 +1725,7 @@ 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); @@ -1694,6 +1735,7 @@ 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); .... @@ -1709,6 +1751,7 @@ 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 { @@ -1794,6 +1837,7 @@ 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())) .... @@ -1811,6 +1855,7 @@ every application class. Finally, to run this version of tracing, go to the directory `examples` and type: +[source, text] .... ajc -argfile tracing/tracev3.lst .... @@ -1819,12 +1864,14 @@ 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) diff --git a/docs/progGuideDB/gettingstarted.adoc b/docs/progGuideDB/gettingstarted.adoc index 0dbb85056..f22c9cefd 100644 --- a/docs/progGuideDB/gettingstarted.adoc +++ b/docs/progGuideDB/gettingstarted.adoc @@ -39,7 +39,7 @@ testing and performance tuning of applications. And, in the section following, xref:#starting-production[Production Aspects], we present aspects that implement crosscutting functionality common in Java applications. We will defer discussing a third category of aspects, -reusable aspects, until xref:language.adoc[The AspectJ Language]. +reusable aspects, until xref:language.adoc#language[The AspectJ Language]. These categories are informal, and this ordering is not the only way to adopt AspectJ. Some developers may want to use a production aspect right @@ -56,7 +56,7 @@ language, but this is by no means a complete overview of AspectJ. The features are presented using a simple figure editor system. A `Figure` consists of a number of `FigureElements`, which can be either -`Point`s or `Line`s. The `Figure` class provides factory services. There +``Point``s or ``Line``s. The `Figure` class provides factory services. There is also a `Display`. Most example programs later in this chapter are based on this system as well. @@ -143,17 +143,19 @@ context_ of the original call join point. In AspectJ, _pointcuts_ pick out certain join points in the program flow. For example, the pointcut +[source, java] .... call(void Point.setX(int)) .... picks out each join point that is a call to a method that has the -signature `void Point.setX(int)` - that is, `Point`'s void `setX` method +signature `void Point.setX(int)` - that is, ``Point``'s void `setX` method with a single `int` parameter. A pointcut can be built out of other pointcuts with and, or, and not (spelled `&&`, `||`, and `!`). For example: +[source, java] .... call(void Point.setX(int)) || call(void Point.setY(int)) @@ -165,6 +167,7 @@ picks out each join point that is either a call to `setX` or a call to Pointcuts can identify join points from many different types - in other words, they can crosscut types. For example, +[source, java] .... call(void FigureElement.setXY(int,int)) || call(void Point.setX(int)) || @@ -182,6 +185,7 @@ crosscutting concern, it is a bit of a mouthful. So AspectJ allows programmers to define their own named pointcuts with the `pointcut` form. So the following declares a new, named pointcut: +[source, java] .... pointcut move(): call(void FigureElement.setXY(int,int)) || @@ -202,6 +206,7 @@ _property-based_ crosscutting. The simplest of these involve using wildcards in certain fields of the method signature. For example, the pointcut +[source, java] .... call(void Figure.make*(..)) .... @@ -211,16 +216,18 @@ picks out each join point that's a call to a void method defined on parameters. In our system, this picks out calls to the factory methods `makePoint` and `makeLine`. The pointcut +[source, java] .... call(public * Figure.* (..)) .... -picks out each call to `Figure`'s public methods. +picks out each call to ``Figure``'s public methods. But wildcards aren't the only properties AspectJ supports. Another pointcut, `cflow`, identifies join points based on whether they occur in the dynamic context of other join points. So +[source, java] .... cflow(move()) .... @@ -245,6 +252,7 @@ For example, before advice on a method call join point runs before the actual method starts running, just after the arguments to the method call are evaluated. +[source, java] .... before(): move() { System.out.println("about to move"); @@ -260,6 +268,7 @@ of after advice: `after returning`, `after throwing`, and plain `after` (which runs after returning _or_ throwing, like Java's `finally`). +[source, java] .... after() returning: move() { System.out.println("just successfully moved"); @@ -280,6 +289,7 @@ An advice declaration has a parameter list (like a method) that gives names to all the pieces of context that it uses. For example, the after advice +[source, java] .... after(FigureElement fe, int x, int y) returning: ...SomePointcut... { @@ -288,10 +298,11 @@ after(FigureElement fe, int x, int y) returning: .... uses three pieces of exposed context, a `FigureElement` named fe, and -two `int`s named x and y. +two ``int``s named x and y. The body of the advice uses the names just like method parameters, so +[source, java] .... after(FigureElement fe, int x, int y) returning: ...SomePointcut... { @@ -303,6 +314,7 @@ The advice's pointcut publishes the values for the advice's arguments. The three primitive pointcuts `this`, `target` and `args` are used to publish these values. So now we can write the complete piece of advice: +[source, java] .... after(FigureElement fe, int x, int y) returning: call(void FigureElement.setXY(int, int)) @@ -326,6 +338,7 @@ named pointcut is used (by advice, or in another named pointcut), it publishes its context by name just like the `this`, `target` and `args` pointcut. So another way to write the above advice is +[source, java] .... pointcut setXY(FigureElement fe, int x, int y): call(void FigureElement.setXY(int, int)) @@ -360,8 +373,9 @@ Suppose we want to have `Screen` objects observe changes to `Point` objects, where `Point` is an existing class. We can implement this by writing an aspect declaring that the class Point `Point` has an instance field, `observers`, that keeps track of the `Screen` objects that are -observing `Point`s. +observing ``Point``s. +[source, java] .... aspect PointObserving { private Vector Point.observers = new Vector(); @@ -373,6 +387,7 @@ The `observers` field is private, so only `PointObserving` can see it. So observers are added or removed with the static methods `addObserver` and `removeObserver` on the aspect. +[source, java] .... aspect PointObserving { private Vector Point.observers = new Vector(); @@ -391,6 +406,7 @@ Along with this, we can define a pointcut `changes` that defines what we want to observe, and the after advice defines what we want to do when we observe a change. +[source, java] .... aspect PointObserving { private Vector Point.observers = new Vector(); @@ -417,7 +433,7 @@ aspect PointObserving { } .... -Note that neither `Screen`'s nor `Point`'s code has to be modified, and +Note that neither ``Screen``'s nor ``Point``'s code has to be modified, and that all the changes needed to support this new capability are local to this aspect. @@ -435,6 +451,7 @@ aspect instances. By default, each aspect is a singleton, so one aspect instance is created. This means that advice may use non-static fields of the aspect, if it needs to keep state around: +[source, java] .... aspect Logging { OutputStream logStream = System.err; @@ -471,6 +488,7 @@ workings of a program. It is a simple tracing aspect that prints a message at specified method calls. In our figure editor example, one such aspect might simply trace whenever points are drawn. +[source, java] .... aspect SimpleTracing { pointcut tracedCall(): @@ -487,6 +505,7 @@ advice bodies this variable is bound to an object that describes the current join point. The effect of this code is to print a line like the following every time a figure element receives a `draw` method call: +[source, text] .... Entering: call(void FigureElement.draw(GraphicsContext)) .... @@ -529,6 +548,7 @@ For example, the following aspect counts the number of calls to the methods of a `Point` that happen within the control flow of those calls to `rotate`: +[source, java] .... aspect SetsInRotateCounting { int rotateCount = 0; @@ -589,6 +609,7 @@ properly do the work they are supposed to. AspectJ makes it possible to implement pre- and post-condition testing in modular form. For example, this code +[source, java] .... aspect PointBoundsChecking { @@ -636,6 +657,7 @@ the constraint that only the well-known factory methods can add an element to the registry of figure elements. Enforcing this constraint ensures that no figure element is added to the registry more than once. +[source, java] .... aspect RegistrationProtection { @@ -662,6 +684,7 @@ This advice throws a runtime exception at certain join points, but AspectJ can do better. Using the `declare error` form, we can have the _compiler_ signal the error. +[source, java] .... aspect RegistrationProtection { @@ -722,6 +745,7 @@ state of the dirty flag and resets it to false. The pointcut `move` captures all the method calls that can move a figure element. The after advice on `move` sets the dirty flag whenever an object moves. +[source, java] .... aspect MoveTracking { private static boolean dirty = false; @@ -807,6 +831,7 @@ modular way. The following code adds after advice that runs only when the factory methods of `Figure` are called in the control flow of a method on a `ColorControllingClient`. +[source, java] .... aspect ColorControl { pointcut CCClientCflow(ColorControllingClient client): @@ -841,6 +866,7 @@ captures the public method calls of the package, and the after advice runs whenever one of those calls throws an Error. The advice logs that Error and then the throw resumes. +[source, java] .... aspect PublicErrorLogging { Log log = new Log(); @@ -861,6 +887,7 @@ outermost call into the `com.bigboxco` package and the re-entrant call. The `cflow` primitive pointcut can be used in a nice way to exclude these re-entrant calls: +[source, java] .... after() throwing (Error e): publicMethodCall() && !cflow(publicMethodCall()) { @@ -874,6 +901,7 @@ individual methods handle each of the different kinds of elements that must be parsed. They have names like `parseMethodDec`, `parseThrows`, and `parseExpr`. +[source, java] .... aspect ContextFilling { pointcut parse(JavaParser jp): diff --git a/docs/progGuideDB/idioms.adoc b/docs/progGuideDB/idioms.adoc index 9bc16c081..8e5f6e43e 100644 --- a/docs/progGuideDB/idioms.adoc +++ b/docs/progGuideDB/idioms.adoc @@ -11,6 +11,7 @@ Here's an example of how to enfore a rule that code in the java.sql package can only be used from one particular package in your system. This doesn't require any access to code in the java.sql package. +[source, java] .... /* Any call to methods or constructors in java.sql */ pointcut restrictedCall(): @@ -27,6 +28,7 @@ declare error: restrictedCall() && illegalSource(): Any call to an instance of a subtype of AbstractFacade whose class is not exactly equal to AbstractFacade: +[source, java] .... pointcut nonAbstract(AbstractFacade af): call(* *(..)) @@ -37,6 +39,7 @@ pointcut nonAbstract(AbstractFacade af): If AbstractFacade is an abstract class or an interface, then every instance must be of a subtype and you can replace this with: +[source, java] .... pointcut nonAbstract(AbstractFacade af): call(* *(..)) @@ -46,6 +49,7 @@ pointcut nonAbstract(AbstractFacade af): Any call to a method which is defined by a subtype of AbstractFacade, but which isn't defined by the type AbstractFacade itself: +[source, java] .... pointcut callToUndefinedMethod(): call(* AbstractFacade+.*(..)) @@ -55,6 +59,7 @@ pointcut callToUndefinedMethod(): The execution of a method that is defined in the source code for a type that is a subtype of AbstractFacade but not in AbstractFacade itself: +[source, java] .... pointcut executionOfUndefinedMethod(): execution(* *(..)) diff --git a/docs/progGuideDB/implementation.adoc b/docs/progGuideDB/implementation.adoc index a34324d5f..f7a8f7c7b 100644 --- a/docs/progGuideDB/implementation.adoc +++ b/docs/progGuideDB/implementation.adoc @@ -17,6 +17,7 @@ implementations should do tomorrow. According to the AspectJ language semantics, the declaration +[source, java] .... before(): get(int Point.x) { System.out.println("got x"); } .... @@ -128,6 +129,7 @@ source code. This means that there may be call join points to `Class.forName` or `StringBuffer.append` from programs that do not, at first glance, appear to contain such calls: +[source, java] .... class Test { void main(String[] args) { @@ -158,6 +160,7 @@ be the case. Before advice is allowed. The second is that the control flow of a handler join point is not picked out. For example, the following pointcut +[source, java] .... cflow(call(void foo()) || handler(java.io.IOException)) .... @@ -173,6 +176,7 @@ This does not restrict programs from placing before advice on handlers inside _other_ control flows. This advice, for example, is perfectly fine: +[source, java] .... before(): handler(java.io.IOException) && cflow(void parse()) { System.out.println("about to handle an exception while parsing"); @@ -187,6 +191,7 @@ have fewer such restrictions. The code for Java initializers, such as the assignment to the field d in +[source, java] .... class C { double d = Math.sqrt(2); @@ -203,6 +208,7 @@ a super-constructor (as opposed to a `this` constructor), the target type's initialization code will _not_ be run when that inter-type constructor is called. +[source, java] .... aspect A { C.new(Object o) {} // implicitly calls super() diff --git a/docs/progGuideDB/language.adoc b/docs/progGuideDB/language.adoc index f48cc4290..3aeb5eebd 100644 --- a/docs/progGuideDB/language.adoc +++ b/docs/progGuideDB/language.adoc @@ -24,30 +24,31 @@ exposed to the new terminology introduced by AspectJ. Here's an example of an aspect definition in AspectJ: -.... - 1 aspect FaultHandler { - 2 - 3 private boolean Server.disabled = false; - 4 - 5 private void reportFault() { - 6 System.out.println("Failure! Please fix it."); - 7 } - 8 - 9 public static void fixServer(Server s) { -10 s.disabled = false; -11 } -12 -13 pointcut services(Server s): target(s) && call(public * *(..)); -14 -15 before(Server s): services(s) { -16 if (s.disabled) throw new DisabledException(); -17 } -18 -19 after(Server s) throwing (FaultException e): services(s) { -20 s.disabled = true; -21 reportFault(); -22 } -23 } +[source, java] +.... +/*01*/ aspect FaultHandler { +/*02*/ +/*03*/ private boolean Server.disabled = false; +/*04*/ +/*05*/ private void reportFault() { +/*06*/ System.out.println("Failure! Please fix it."); +/*07*/ } +/*08*/ +/*09*/ public static void fixServer(Server s) { +/*10*/ s.disabled = false; +/*11*/ } +/*12*/ +/*13*/ pointcut services(Server s): target(s) && call(public * *(..)); +/*14*/ +/*15*/ before(Server s): services(s) { +/*16*/ if (s.disabled) throw new DisabledException(); +/*17*/ } +/*18*/ +/*19*/ after(Server s) throwing (FaultException e): services(s) { +/*20*/ s.disabled = true; +/*21*/ reportFault(); +/*22*/ } +/*23*/ } .... The `FaultHandler` consists of one inter-type field on `Server` (line @@ -69,6 +70,7 @@ invocations and executions, the handling of exceptions, field assignments and accesses, etc. Take, for example, the pointcut definition in line 13: +[source, java] .... pointcut services(Server s): target(s) && call(public * *(..)) .... @@ -114,6 +116,7 @@ define aspect implementation that runs at join points picked out by the pointcut. For example, the advice in lines 15-17 specifies that the following piece of code +[source, java] .... { if (s.disabled) throw new DisabledException(); @@ -128,6 +131,7 @@ corresponding methods are executed. The advice in lines 19-22 defines another piece of implementation that is executed on the same pointcut: +[source, java] .... { s.disabled = true; @@ -148,6 +152,7 @@ this guide. Consider the following Java class: +[source, java] .... class Point { private int x, y; @@ -166,6 +171,7 @@ In order to get an intuitive understanding of AspectJ's join points and pointcuts, let's go back to some of the basic principles of Java. Consider the following a method declaration in class Point: +[source, java] .... void setX(int x) { this.x = x; } .... @@ -192,6 +198,7 @@ complete listing.) Pointcuts pick out these join points. For example, the pointcut +[source, java] .... pointcut setter(): target(Point) && (call(void setX(int)) || @@ -201,6 +208,7 @@ pointcut setter(): target(Point) && picks out each call to `setX(int)` or `setY(int)` when called on an instance of `Point`. Here's another example: +[source, java] .... pointcut ioHandler(): within(MyClass) && handler(IOException); .... @@ -230,7 +238,7 @@ when the target object is of type `SomeType`:: `target(SomeType)` when the executing code belongs to class `MyClass`:: `within(MyClass)` -when the join point is in the control flow of a call to a `Test`'s +when the join point is in the control flow of a call to a ``Test``'s no-argument `main` method:: `cflow(call(void Test.main()))` @@ -256,9 +264,9 @@ such `set` method; this pointcut picks out calls to all of them. means (1) the execution of any method with no parameters that returns an `int`, (2) the call to any `setY` method that takes a `long` as an argument, regardless of return type or declaring type, (3) the call to -any of `Point`'s `setY` methods that take an `int` as an argument, +any of ``Point``'s `setY` methods that take an `int` as an argument, regardless of return type, and (4) the call to any classes' constructor, -so long as it takes exactly two `int`s as arguments. +so long as it takes exactly two ``int``s as arguments. * You can compose pointcuts. For example, [arabic] . `target(Point) && call(int *())` @@ -270,7 +278,7 @@ so long as it takes exactly two `int`s as arguments. + means (1) any call to an `int` method with no arguments on an instance of `Point`, regardless of its name, (2) any call to any method where the -call is made from the code in `Point`'s or `Line`'s type declaration, +call is made from the code in ``Point``'s or ``Line``'s type declaration, (3) the execution of any constructor taking exactly one `int` argument, regardless of where the call is made from, and (4) any method call to an `int` method when the executing object is any type except `Point`. @@ -286,12 +294,13 @@ non-static method, and (3) any execution of a public, non-static method. * Pointcuts can also deal with interfaces. For example, given the interface + +[source, java] .... interface MyInterface { ... } .... + the pointcut `call(* MyInterface.*(..))` picks out any call to a method -in `MyInterface`'s signature -- that is, any method defined by +in ``MyInterface``'s signature -- that is, any method defined by `MyInterface` or inherited by one of its a supertypes. [[call-vs-execution]] @@ -339,6 +348,7 @@ primitive pointcuts like `cflow` and `cflowbelow`. Here's an example: `cflow(P)` picks out each join point in the control flow of the join points picked out by <P>. So, pictorially: +[source, text] .... P --------------------- \ @@ -350,6 +360,7 @@ What does `cflow(P) && cflow(Q)` pick out? Well, it picks out each join point that is in both the control flow of <P> and in the control flow of <Q>. So... +[source, text] .... P --------------------- \ @@ -370,6 +381,7 @@ But what does `cflow(P && Q)` mean? Well, it means the control flow of those join points that are both picked out by <P> and picked out by <Q>. +[source, text] .... P && Q ------------------- \ @@ -384,6 +396,7 @@ control flow of `(P && Here's some code that expresses this. +[source, java] .... public class Test { public static void main(String[] args) { @@ -422,6 +435,7 @@ for more details.) Consider again the first pointcut definition in this chapter: +[source, java] .... pointcut setter(): target(Point) && (call(void setX(int)) || @@ -435,6 +449,7 @@ side. An empty parameter list means that none of the context from the join points is published from this pointcut. But consider another version of version of this pointcut definition: +[source, java] .... pointcut setter(Point p): target(p) && (call(void setX(int)) || @@ -452,6 +467,7 @@ matched join point. Here's another example that illustrates the flexible mechanism for defining pointcut parameters: +[source, java] .... pointcut testEquality(Point p): target(Point) && args(p) && @@ -464,10 +480,11 @@ access to a `Point` from each join point. But in this case, looking at the right-hand side we find that the object named in the parameters is not the target `Point` object that receives the call; it's the argument (also of type `Point`) passed to the `equals` method when some other -`Point` is the target. If we wanted access to both `Point`s, then the +`Point` is the target. If we wanted access to both ``Point``s, then the pointcut definition that would expose target `Point p1` and argument `Point p2` would be +[source, java] .... pointcut testEquality(Point p1, Point p2): target(p1) && args(p2) && @@ -476,6 +493,7 @@ pointcut testEquality(Point p1, Point p2): target(p1) && Let's look at another variation of the `setters` pointcut: +[source, java] .... pointcut setter(Point p, int newval): target(p) && args(newval) && @@ -493,6 +511,7 @@ important rule is that all the pointcut parameters must be bound at every join point picked out by the pointcut. So, for example, the following pointcut definition will result in a compilation error: +[source, java] .... pointcut badPointcut(Point p1, Point p2): (target(p1) && call(void setX(int))) || @@ -512,6 +531,7 @@ operations to their `Partner` objects. The aspect `HandleLiveness` ensures that, before the delegations, the partner exists and is alive, or else it throws an exception. +[source, java] .... class Handle { Partner partner = new Partner(); @@ -594,6 +614,7 @@ given either by named pointcuts (like the ones you've seen above) or by anonymous pointcuts. Here is an example of an advice on a named pointcut: +[source, java] .... pointcut setter(Point p1, int newval): target(p1) && args(newval) (call(void setX(int) || @@ -607,6 +628,7 @@ before(Point p1, int newval): setter(p1, newval) { And here is exactly the same example, but using an anonymous pointcut: +[source, java] .... before(Point p1, int newval): target(p1) && args(newval) (call(void setX(int)) || @@ -621,6 +643,7 @@ Here are examples of the different advice: This before advice runs just before the join points picked out by the (anonymous) pointcut: +[source, java] .... before(Point p, int x): target(p) && args(x) && call(void setX(int)) { if (!p.assertX(x)) return; @@ -631,6 +654,7 @@ This after advice runs just after each join point picked out by the (anonymous) pointcut, regardless of whether it returns normally or throws an exception: +[source, java] .... after(Point p, int x): target(p) && args(x) && call(void setX(int)) { if (!p.assertX(x)) throw new PostConditionViolation(); @@ -642,6 +666,7 @@ by the (anonymous) pointcut, but only if it returns normally. The return value can be accessed, and is named `x` here. After the advice runs, the return value is returned: +[source, java] .... after(Point p) returning(int x): target(p) && call(int getX()) { System.out.println("Returning int value " + x + " for p = " + p); @@ -653,6 +678,7 @@ the (anonymous) pointcut, but only when it throws an exception of type `Exception`. Here the exception value can be accessed with the name `e`. The advice re-raises the exception after it's done: +[source, java] .... after() throwing(Exception e): target(Point) && call(void setX(int)) { System.out.println(e); @@ -663,6 +689,7 @@ This around advice traps the execution of the join point; it runs _instead_ of the join point. The original action associated with the join point can be invoked through the special `proceed` call: +[source, java] .... void around(Point p, int x): target(p) && args(x) @@ -683,6 +710,7 @@ class. Here are examples of some such inter-type declarations: This declares that each `Server` has a `boolean` field named `disabled`, initialized to `false`: +[source, java] .... private boolean Server.disabled = false; .... @@ -696,6 +724,7 @@ another aspect) there won't be a name collision, since no reference to This declares that each `Point` has an `int` method named `getX` with no arguments that returns whatever `this.x` is: +[source, java] .... public int Point.getX() { return this.x; } .... @@ -707,6 +736,7 @@ conflict. This publically declares a two-argument constructor for `Point`: +[source, java] .... public Point.new(int x, int y) { this.x = x; this.y = y; } .... @@ -714,6 +744,7 @@ public Point.new(int x, int y) { this.x = x; this.y = y; } This publicly declares that each `Point` has an `int` field named `x`, initialized to zero: +[source, java] .... public int Point.x = 0; .... @@ -724,6 +755,7 @@ has a field named `x` (defined by `Point` or by another aspect). This declares that the `Point` class implements the `Comparable` interface: +[source, java] .... declare parents: Point implements Comparable; .... @@ -734,6 +766,7 @@ required by `Comparable`. This declares that the `Point` class extends the `GeometricObject` class. +[source, java] .... declare parents: Point extends GeometricObject; .... @@ -741,6 +774,7 @@ declare parents: Point extends GeometricObject; An aspect can have several inter-type declarations. For example, the following declarations +[source, java] .... public String Point.name; public void Point.setName(String name) { this.name = name; } @@ -754,6 +788,7 @@ An inter-type member can only have one target type, but often you may wish to declare the same member on more than one type. This can be done by using an inter-type member in combination with a private interface: +[source, java] .... aspect A { private interface HasName {} @@ -782,18 +817,20 @@ declarations in addition to public inter-type declarations. Private means private in relation to the aspect, not necessarily the target type. So, if an aspect makes a private inter-type declaration of a field +[source, java] .... private int Foo.x; .... -Then code in the aspect can refer to `Foo`'s `x` field, but nobody else +Then code in the aspect can refer to ``Foo``'s `x` field, but nobody else can. Similarly, if an aspect makes a package-protected introduction, +[source, java] .... - int Foo.x; +int Foo.x; .... -then everything in the aspect's package (which may or may not be `Foo`'s +then everything in the aspect's package (which may or may not be ``Foo``'s package) can access `x`. ==== Example: `PointAssertions` @@ -806,6 +843,7 @@ other parts of the program (including the code in `Point`) have no business accessing the assert methods. Only the code inside of the aspect can call those methods. +[source, java] .... class Point { int x, y; @@ -856,6 +894,7 @@ One way to use it is simply to print it out. Like all Java objects, `thisJoinPoint` has a `toString()` method that makes quick-and-dirty tracing easy: +[source, java] .... aspect TraceNonStaticMethods { before(Point p): target(p) && call(* *(..)) { @@ -868,6 +907,7 @@ The type of `thisJoinPoint` includes a rich reflective class hierarchy of signatures, and can be used to access both static and dynamic information about join points such as the arguments of the join point: +[source, java] .... thisJoinPoint.getArgs() .... @@ -876,6 +916,7 @@ In addition, it holds an object consisting of all the static information about the join point such as corresponding line number and static signature: +[source, java] .... thisJoinPoint.getStaticPart() .... @@ -888,6 +929,7 @@ necessary when using `thisJoinPoint` directly. It is always the case that +[source, java] .... thisJoinPointStaticPart == thisJoinPoint.getStaticPart() @@ -903,8 +945,9 @@ but it is not the current but the enclosing join point. So, for example, it is possible to print out the calling source location (if available) with +[source, java] .... - before() : execution (* *(..)) { - System.err.println(thisEnclosingJoinPointStaticPart.getSourceLocation()) - } +before() : execution (* *(..)) { + System.err.println(thisEnclosingJoinPointStaticPart.getSourceLocation()) +} .... diff --git a/docs/progGuideDB/pitfalls.adoc b/docs/progGuideDB/pitfalls.adoc index b1769ce26..0bbe6f5f5 100644 --- a/docs/progGuideDB/pitfalls.adoc +++ b/docs/progGuideDB/pitfalls.adoc @@ -11,6 +11,7 @@ surprising behavior and how to understand them. Here is a Java program with peculiar behavior +[source, java] .... public class Main { public static void main(String[] args) { @@ -39,6 +40,7 @@ the completely silent abort. The following short aspect will also generate this behavior: +[source, java] .... aspect A { before(): call(* *(..)) { System.out.println("before"); } @@ -50,6 +52,7 @@ Why? Because the call to println is also a call matched by the pointcut `call (* *(..))`. We get no output because we used simple after() advice. If the aspect were changed to +[source, java] .... aspect A { before(): call(* *(..)) { System.out.println("before"); } @@ -65,6 +68,7 @@ There's a simple idiom to use if you ever have a worry that your advice might apply in this way. Just restrict the advice from occurring in join points caused within the aspect. So: +[source, java] .... aspect A { before(): call(* *(..)) && !within(A) { System.out.println("before"); } @@ -75,6 +79,7 @@ aspect A { Other solutions might be to more closely restrict the pointcut in other ways, for example: +[source, java] .... aspect A { before(): call(* MyObject.*(..)) { System.out.println("before"); } diff --git a/docs/progGuideDB/quickreference.adoc b/docs/progGuideDB/quickreference.adoc index 01b152e31..6d8febc56 100644 --- a/docs/progGuideDB/quickreference.adoc +++ b/docs/progGuideDB/quickreference.adoc @@ -9,16 +9,16 @@ |*Methods and Constructors* | |`call(Signature)` |every call to any method or constructor matching -<Signature> at the call site +`<Signature>` at the call site |`execution(Signature)` |every execution of any method or constructor -matching <Signature> +matching `<Signature>` |*Fields* | -|`get(Signature)` |every reference to any field matching <Signature> +|`get(Signature)` |every reference to any field matching `<Signature>` -|`set(Signature)` |every assignment to any field matching <Signature>. +|`set(Signature)` |every assignment to any field matching `<Signature>`. The assigned value can be exposed with an `args` pointcut |*Exception Handlers* | @@ -37,12 +37,12 @@ pointcut initializer for any type in <TypePattern> |`initialization(Signature)` |every initialization of an object when the -first constructor called in the type matches <Signature>, encompassing +first constructor called in the type matches `<Signature>`, encompassing the return from the super constructor call to the return of the first-called constructor |`preinitialization(Signature)` |every pre-initialization of an object -when the first constructor called in the type matches <Signature>, +when the first constructor called in the type matches `<Signature>`, encompassing the entry of the first-called constructor to the call to the super constructor @@ -52,7 +52,7 @@ the super constructor <TypePattern> |`withincode(Signature)` |every join point from code defined in a method -or constructor matching <Signature> +or constructor matching `<Signature>` |=== [[quick-typePatterns]] @@ -62,290 +62,118 @@ A type pattern is one of [cols=",",] |=== -|<TypeNamePattern> |all types in <TypeNamePattern> +|*Type pattern* | -|<SubtypePattern> |all types in <SubtypePattern>, a pattern with a +. +|`<TypeNamePattern>` |all types in `<TypeNamePattern>` -|<ArrayTypePattern> |all types in <ArrayTypePattern>, a pattern with one -or more []s. +|`<SubtypePattern>` |all types in `<SubtypePattern>`, a pattern with a `+` + +|`<ArrayTypePattern>` |all types in `<ArrayTypePattern>`, a pattern with one or more ``[]``s. |`!TypePattern` |all types not in <TypePattern> -|`TypePattern0 - && TypePattern1` |all types in both <TypePattern0> and -<TypePattern1> +|`TypePattern0 && TypePattern1` |all types in both `<TypePattern0>` and `<TypePattern1>` -|`TypePattern0 || TypePattern1` |all types in either <TypePattern0> or -<TypePattern1> +|`TypePattern0 \|\| TypePattern1` |all types in either `<TypePattern0>` or `<TypePattern1>` -|`( TypePattern )` |all types in <TypePattern> +|`( TypePattern )` |all types in `<TypePattern>` |=== -where <TypeNamePattern> can either be a plain type name, the wildcard -`*` (indicating all types), or an identifier with embedded `*` and `..` +where `<TypeNamePattern>` can either be a plain type name, the wildcard +`\*` (indicating all types), or an identifier with embedded `*` and `..` wildcards. An embedded `*` in an identifier matches any sequence of characters, but -does not match the package (or inner-type) separator ".". +does not match the package (or inner-type) separator `.`. An embedded `..` in an identifier matches any sequence of characters -that starts and ends with the package (or inner-type) separator ".". +that starts and ends with the package (or inner-type) separator `.`. [[quick-advice]] === Advice Each piece of advice is of the form -____ -[ strictfp ] - -AdviceSpec - -[ throws - -TypeList - -] : - -Pointcut +[source, text] +.... +[ strictfp ] AdviceSpec [ throws TypeList ] : Pointcut { Body } +.... -\{ +where `<AdviceSpec>` is one of -Body - -} -____ - -where <AdviceSpec> is one of - -`before( Formals ) `:: +`before( Formals )`:: runs before each join point -`after( Formals ) returning - [ ( Formal ) ] `:: +`after( Formals ) returning [ ( Formal ) ]`:: runs after each join point that returns normally. The optional formal gives access to the returned value -`after( Formals ) throwing [ - ( Formal ) ] `:: - runs after each join point that throws a - + - Throwable - + - . If the optional formal is present, runs only after each join point - that throws a - + - Throwable - + - of the type of - + - Formal - + - , and - + - Formal - + - gives access to the - + - Throwable - + - exception value -`after( Formals ) `:: +`after( Formals ) throwing [ ( Formal ) ]`:: + runs after each join point that throws a `Throwable`. + If the optional formal is present, runs only after each join point + that throws a `Throwable` of the type of `Formal`, and `Formal` gives access to the + `Throwable` exception value +`after( Formals )`:: runs after each join point regardless of whether it returns normally - or throws a - + - Throwable -`Type - around( Formals ) `:: + or throws a `Throwable` +`Type around( Formals )`:: runs in place of each join point. The join point can be executed by - calling - + - proceed - + - , which takes the same number and types of arguments as the around + calling `proceed`, which takes the same number and types of arguments as the around advice. Three special variables are available inside of advice bodies: `thisJoinPoint`:: - an object of type - + - org.aspectj.lang.JoinPoint - + - representing the join point at which the advice is executing. + an object of type `org.aspectj.lang.JoinPoint` representing the join point + at which the advice is executing `thisJoinPointStaticPart`:: - equivalent to - + - thisJoinPoint.getStaticPart() - + - , but may use fewer runtime resources. + equivalent to `thisJoinPoint.getStaticPart()`, but may use fewer runtime resources `thisEnclosingJoinPointStaticPart`:: - the static part of the dynamically enclosing join point. + the static part of the dynamically enclosing join point [[quick-interType]] === Inter-type member declarations Each inter-type member is one of -` - Modifiers ReturnType OnType . Id - ( Formals ) - [ throws TypeList ] - { Body } - `:: - a method on - + - OnType - + - . -` - abstract Modifiers ReturnType OnType . Id - ( Formals ) - [ throws TypeList ] ; - `:: - an abstract method on - + - OnType - + - . -` - Modifiers OnType . new - ( Formals ) - [ throws TypeList ] - { Body } - `:: - a constructor on - + - OnType - + - . -` - Modifiers Type OnType . Id - [ = Expression ] ; - `:: - a field on - + - OnType - + - . +`Modifiers ReturnType OnType . Id ( Formals ) [ throws TypeList ] { Body }`:: + a method on `OnType` +`abstract Modifiers ReturnType OnType . Id ( Formals ) [ throws TypeList ] ;`:: + an abstract method on `OnType` +`Modifiers OnType . new ( Formals ) [ throws TypeList ] { Body }`:: + a constructor on `OnType` +`Modifiers Type OnType . Id [ = Expression ] ;`:: + a field on `OnType` [[quick-other]] === Other declarations -` - declare parents : - TypePattern extends - Type ; - `:: - the types in - + - TypePattern - + - extend - + - Type - + - . -` - declare parents : TypePattern - implements TypeList ; - `:: - the types in - + - TypePattern - + - implement the types in - + - TypeList - + - . -` - declare warning : Pointcut : - String ; - `:: - if any of the join points in - + - Pointcut - + - possibly exist in the program, the compiler emits the warning - + - String - + - . -` - declare error : Pointcut : - String ; - `:: - if any of the join points in - + - Pointcut - + - could possibly exist in the program, the compiler emits the error - + - String - + - . -` - declare soft : - Type : - Pointcut ; - `:: - any - + - Type - + - exception that gets thrown at any join point picked out by - + - Pointcut - + - is wrapped in - + - org.aspectj.lang.SoftException - + - . -` - declare precedence : - TypePatternList ; - `:: +`declare parents : TypePattern extends Type ;`:: + the types in `TypePattern` extend `Type` +`declare parents : TypePattern implements TypeList ;`:: + the types in `TypePattern` implement the types in `TypeList` +`declare warning : Pointcut : String ;`:: + if any of the join points in `Pointcut` possibly exist in the program, + the compiler emits the warning `String` +`declare error : Pointcut : String ;`:: + if any of the join points in `Pointcut` could possibly exist in the program, + the compiler emits the error `String` +`declare soft : Type : Pointcut ;`:: + any `Type` exception that gets thrown at any join point picked out by `Pointcut` + is wrapped in `org.aspectj.lang.SoftException` +`declare precedence : TypePatternList ;`:: at any join point where multiple pieces of advice apply, the advice - precedence at that join point is in - + - TypePatternList - + - order. + precedence at that join point is in `TypePatternList` order [[quick-aspectAssociations]] === Aspects Each aspect is of the form -____ -[ privileged ] - -Modifiers - -aspect - -Id - -[ extends - -Type - -] [ implements - -TypeList - -] [ - -PerClause - -] \{ - -Body - -} -____ +[source, text] +.... +[ privileged ] Modifiers aspect Id [ extends Type ] [ implements TypeList ] [ PerClause ] { Body } +.... -where <PerClause> defines how the aspect is instantiated and associated +where `<PerClause>` defines how the aspect is instantiated and associated (`issingleton()` by default): [cols=",,",options="header",] @@ -355,18 +183,18 @@ where <PerClause> defines how the aspect is instantiated and associated default. |`aspectOf()` at all join points |`perthis(Pointcut)` |An instance is associated with each object that is -the currently executing object at any join point in <Pointcut>. +the currently executing object at any join point in `<Pointcut>`. |`aspectOf(Object)` at all join points |`pertarget(Pointcut)` |An instance is associated with each object that -is the target object at any join point in <Pointcut>. +is the target object at any join point in `<Pointcut>`. |`aspectOf(Object)` at all join points |`percflow(Pointcut)` |The aspect is defined for each entrance to the -control flow of the join points defined by <Pointcut>. |`aspectOf()` at +control flow of the join points defined by `<Pointcut>`. |`aspectOf()` at join points in `cflow(Pointcut)` |`percflowbelow(Pointcut)` |The aspect is defined for each entrance to -the control flow below the join points defined by <Pointcut>. +the control flow below the join points defined by `<Pointcut>`. |`aspectOf()` at join points in `cflowbelow(Pointcut)` |=== diff --git a/docs/progGuideDB/semantics.adoc b/docs/progGuideDB/semantics.adoc index 1943aa609..aa46cf18d 100644 --- a/docs/progGuideDB/semantics.adoc +++ b/docs/progGuideDB/semantics.adoc @@ -63,11 +63,7 @@ Constructor call:: Constructor execution:: When the body of code for an actual constructor executes, after its this or super constructor call. The object being constructed is the - currently executing object, and so may be accessed with the - + - this - + - pointcut. The constructor execution join point for a constructor that + currently executing object, and so may be accessed with the `_this_` pointcut. The constructor execution join point for a constructor that calls a super constructor also includes any non-static initializers of enclosing class. No value is returned from a constructor execution join point, so its return type is considered to be void. @@ -80,15 +76,7 @@ Object pre-initialization:: This encompasses the time between the start of its first called constructor and the start of its parent's constructor. Thus, the execution of these join points encompass the join points of the - evaluation of the arguments of - + - this() - + - and - + - super() - + - constructor calls. No value is returned from an object + evaluation of the arguments of `_this()_` and `_super()_` constructor calls. No value is returned from an object pre-initialization join point, so its return type is considered to be void. Object initialization:: @@ -97,11 +85,7 @@ Object initialization:: and the return of its first called constructor. It includes all the dynamic initializers and constructors used to create the object. The object being constructed is the currently executing object, and so may - be accessed with the - + - this - + - pointcut. No value is returned from a constructor execution join + be accessed with the `_this_` pointcut. No value is returned from a constructor execution join point, so its return type is considered to be void. Field reference:: When a non-constant field is referenced. [Note that references to @@ -180,219 +164,84 @@ build up other pointcuts. The primitive pointcuts and combinators provided by the language are: `call(MethodPattern)`:: - Picks out each method call join point whose signature matches - + - MethodPattern - + - . + Picks out each method call join point whose signature matches `_MethodPattern_`. `execution(MethodPattern)`:: - Picks out each method execution join point whose signature matches - + - MethodPattern - + - . + Picks out each method execution join point whose signature matches `_MethodPattern_`. `get(FieldPattern)`:: - Picks out each field reference join point whose signature matches - + - FieldPattern - + - . [Note that references to constant fields (static final fields bound + Picks out each field reference join point whose signature matches `_FieldPattern_`. [Note that references to constant fields (static final fields bound to a constant string object or primitive value) are not join points, since Java requires them to be inlined.] `set(FieldPattern)`:: - Picks out each field set join point whose signature matches - + - FieldPattern - + - . [Note that the initializations of constant fields (static final + Picks out each field set join point whose signature matches `_FieldPattern_`. [Note that the initializations of constant fields (static final fields where the initializer is a constant string object or primitive value) are not join points, since Java requires their references to be inlined.] `call(ConstructorPattern)`:: - Picks out each constructor call join point whose signature matches - + - ConstructorPattern - + - . + Picks out each constructor call join point whose signature matches `_ConstructorPattern_`. `execution(ConstructorPattern)`:: Picks out each constructor execution join point whose signature - matches - + - ConstructorPattern - + - . + matches `_ConstructorPattern_`. `initialization(ConstructorPattern)`:: Picks out each object initialization join point whose signature - matches - + - ConstructorPattern - + - . + matches `_ConstructorPattern_`. `preinitialization(ConstructorPattern)`:: Picks out each object pre-initialization join point whose signature - matches - + - ConstructorPattern - + - . + matches `_ConstructorPattern_`. `staticinitialization(TypePattern)`:: Picks out each static initializer execution join point whose signature - matches - + - TypePattern - + - . + matches `_TypePattern_`. `handler(TypePattern)`:: - Picks out each exception handler join point whose signature matches - + - TypePattern - + - . + Picks out each exception handler join point whose signature matches `_TypePattern_`. `adviceexecution()`:: Picks out all advice execution join points. `within(TypePattern)`:: Picks out each join point where the executing code is defined in a - type matched by - + - TypePattern - + - . + type matched by `_TypePattern_`. `withincode(MethodPattern)`:: Picks out each join point where the executing code is defined in a - method whose signature matches - + - MethodPattern - + - . + method whose signature matches `_MethodPattern_`. `withincode(ConstructorPattern)`:: Picks out each join point where the executing code is defined in a - constructor whose signature matches - + - ConstructorPattern - + - . + constructor whose signature matches `_ConstructorPattern_`. `cflow(Pointcut)`:: - Picks out each join point in the control flow of any join point - + - P - + - picked out by - + - Pointcut - + - , including - + - P - + - itself. + Picks out each join point in the control flow of any join point `_P_` picked out by `_Pointcut_` , including `_P_` itself. `cflowbelow(Pointcut)`:: - Picks out each join point in the control flow of any join point - + - P - + - picked out by - + - Pointcut - + - , but not - + - P - + - itself. + Picks out each join point in the control flow of any join point `_P_` picked out by `_Pointcut_`, but not `_P_` itself. `this(Type or Id)`:: Picks out each join point where the currently executing object (the - object bound to - + - this - + - ) is an instance of - + - Type - + - , or of the type of the identifier - + - Id - + - (which must be bound in the enclosing advice or pointcut definition). + object bound to `_this_`) is an instance of `_Type_` , or of the type of the identifier `_Id_` (which must be bound in the enclosing advice or pointcut definition). Will not match any join points from static contexts. `target(Type or Id)`:: Picks out each join point where the target object (the object on which - a call or field operation is applied to) is an instance of - + - Type - + - , or of the type of the identifier - + - Id - + - (which must be bound in the enclosing advice or pointcut definition). + a call or field operation is applied to) is an instance of `_Type_` , or of the type of the identifier `_Id_` (which must be bound in the enclosing advice or pointcut definition). Will not match any calls, gets, or sets of static members. `args(Type or Id, ...)`:: Picks out each join point where the arguments are instances of the - appropriate type (or type of the identifier if using that form). A - + - null - + - argument is matched iff the static type of the argument (declared + appropriate type (or type of the identifier if using that form). A `_null_` argument is matched iff the static type of the argument (declared parameter type or field type) is the same as, or a subtype of, the specified args type. `PointcutId(TypePattern or Id, ...)`:: Picks out each join point that is picked out by the user-defined - pointcut designator named by - + - PointcutId - + - . + pointcut designator named by `_PointcutId_` . `if(BooleanExpression)`:: - Picks out each join point where the boolean expression evaluates to - + - true - + - . The boolean expression used can only access static members, - parameters exposed by the enclosing pointcut or advice, and - + - thisJoinPoint - + - forms. In particular, it cannot call non-static methods on the aspect + Picks out each join point where the boolean expression evaluates to `_true_` . The boolean expression used can only access static members, + parameters exposed by the enclosing pointcut or advice, and `_thisJoinPoint_` forms. In particular, it cannot call non-static methods on the aspect or use return values or exceptions exposed by after advice. `! Pointcut`:: - Picks out each join point that is not picked out by - + - Pointcut - + - . + Picks out each join point that is not picked out by `_Pointcut_` . `Pointcut0 && Pointcut1`:: - Picks out each join points that is picked out by both - + - Pointcut0 - + - and - + - Pointcut1 - + - . + Picks out each join points that is picked out by both `_Pointcut0_` and `_Pointcut1_` . `Pointcut0 || Pointcut1`:: - Picks out each join point that is picked out by either pointcuts. - + - Pointcut0 - + - or - + - Pointcut1 - + - . + Picks out each join point that is picked out by either pointcuts. `_Pointcut0_` or `_Pointcut1_` . `( Pointcut )`:: - Picks out each join points picked out by - + - Pointcut - + - . + Picks out each join points picked out by `_Pointcut_` . ==== Pointcut definition Pointcuts are defined and named by the programmer with the `pointcut` declaration. +[source, java] .... pointcut publicIntCall(int i): call(public * *(int)) && args(i); @@ -402,6 +251,7 @@ A named pointcut may be defined in either a class or aspect, and is treated as a member of the class or aspect where it is found. As a member, it may have an access modifier such as `public` or `private`. +[source, java] .... class C { pointcut publicCall(int i): @@ -418,6 +268,7 @@ Pointcuts that are not final may be declared abstract, and defined without a body. Abstract pointcuts may only be declared within abstract aspects. +[source, java] .... abstract aspect A { abstract pointcut publicCall(int i); @@ -426,6 +277,7 @@ abstract aspect A { In such a case, an extending aspect may override the abstract pointcut. +[source, java] .... aspect B extends A { pointcut publicCall(int i): call(public Foo.m(int)) && args(i); @@ -444,6 +296,7 @@ is different than the scope of other members; the scope of other members is the enclosing class _body_. This means that the following code is legal: +[source, java] .... aspect B percflow(publicCall()) { pointcut publicCall(): call(public Foo.m(int)); @@ -466,6 +319,7 @@ collection of types. The pointcut designators that allow this are rather than a type does two things. First, it selects join points as based on the type of the formal parameter. So the pointcut +[source, java] .... pointcut intArg(int i): args(i); .... @@ -477,6 +331,7 @@ advice or pointcut. Values can be exposed from named pointcuts as well, so +[source, java] .... pointcut publicCall(int x): call(public *.*(int)) && intArg(x); pointcut intArg(int i): args(i); @@ -489,6 +344,7 @@ There is one special case for this kind of exposure. Exposing an argument of type Object will also match primitive typed arguments, and expose a "boxed" version of the primitive. So, +[source, java] .... pointcut publicCall(): call(public *.*(..)) && args(Object); .... @@ -496,6 +352,7 @@ pointcut publicCall(): call(public *.*(..)) && args(Object); will pick out all unary methods that take, as their only argument, subtypes of Object (i.e., not primitive types like `int`), but +[source, java] .... pointcut publicCall(Object o): call(public *.*(..)) && args(o); .... @@ -507,6 +364,7 @@ argument was an `int`, then the value passed to advice will be of type The "boxing" of the primitive value is based on the _original_ primitive type. So in the following program +[source, java] .... public class InstanceOf { @@ -569,6 +427,7 @@ field is being set to, so at a set join point, that value can be accessed with an `args` pointcut. So an aspect guarding a static integer variable x declared in type T might be written as +[source, java] .... aspect GuardedX { static final int MAX_CHANGE = 100; @@ -632,6 +491,7 @@ the exception being handled. That value can be accessed with an `args` pointcut. So an aspect used to put `FooException` objects into some normal form before they are handled could be written as +[source, java] .... aspect NormalizeFooException { before(FooException e): handler(FooException) && args(e) { @@ -650,6 +510,7 @@ of advice This can be used, for example, to filter out any join point in the control flow of advice from a particular aspect. +[source, java] .... aspect TraceStuff { pointcut myAdvice(): adviceexecution() && within(TraceStuff); @@ -719,6 +580,7 @@ Object). If it is the "*" wildcard, then any argument will match, and if it is the special wildcard "..", then any number of arguments will match, just like in signature patterns. So the pointcut +[source, java] .... args(int, .., String) .... @@ -762,6 +624,7 @@ Anytime such state is accessed, it is accessed through the _most recent_ control flow that matched. So the "current arg" that would be printed by the following program is zero, even though it is in many control flows. +[source, java] .... class Test { public static void main(String[] args) { @@ -842,6 +705,7 @@ false. Within this expression, the `thisJoinPoint` object is available. So one (extremely inefficient) way of picking out all call join points would be to use the pointcut +[source, java] .... if(thisJoinPoint.getKind().equals("call")) .... @@ -922,6 +786,7 @@ while constructor declarations omit the return type and replace the method name with the class name. The start of a particular method declaration, in class `Test`, for example, might be +[source, java] .... class C { public final void foo() throws ArrayOutOfBoundsException { ... } @@ -931,12 +796,14 @@ class C { In AspectJ, method signature patterns have all these, but most elements can be replaced by wildcards. So +[source, java] .... call(public final void C.foo() throws ArrayOutOfBoundsException) .... picks out call join points to that method, and the pointcut +[source, java] .... call(public final void *.*() throws ArrayOutOfBoundsException) .... @@ -949,12 +816,14 @@ throw `ArrayOutOfBounds` exceptions. The defining type name, if not present, defaults to *, so another way of writing that pointcut would be +[source, java] .... call(public final void *() throws ArrayOutOfBoundsException) .... The wildcard `..` indicates zero or more parameters, so +[source, java] .... execution(void m(..)) .... @@ -962,6 +831,7 @@ execution(void m(..)) picks out execution join points for void methods named `m`, of any number of arguments, while +[source, java] .... execution(void m(.., int)) .... @@ -974,6 +844,7 @@ signature pattern should match methods without a particular modifier, such as all non-public methods, the appropriate modifier should be negated with the `!` operator. So, +[source, java] .... withincode(!public void foo()) .... @@ -981,6 +852,7 @@ withincode(!public void foo()) picks out all join points associated with code in null non-public void methods named `foo`, while +[source, java] .... withincode(void foo()) .... @@ -991,12 +863,14 @@ named `foo`, regardless of access modifier. Method names may contain the * wildcard, indicating any number of characters in the method name. So +[source, java] .... call(int *()) .... picks out all call join points to `int` methods regardless of name, but +[source, java] .... call(int get*()) .... @@ -1009,6 +883,7 @@ than using a particular class name. So the execution join points of private null constructor of a class C defined to throw an ArithmeticException can be picked out with +[source, java] .... execution(private C.new() throws ArithmeticException) .... @@ -1027,6 +902,7 @@ type used to access the method. A common mistake is to specify a declaring type for the `call` pointcut that is a subtype of the originally-declaring type. For example, given the class +[source, java] .... class Service implements Runnable { public void run() { ... } @@ -1035,12 +911,14 @@ class Service implements Runnable { the following pointcut +[source, java] .... call(void Service.run()) .... would fail to pick out the join point for the code +[source, java] .... ((Runnable) new Service()).run(); .... @@ -1049,6 +927,7 @@ Specifying the originally-declaring type is correct, but would pick out any such call (here, calls to the `run()` method of any Runnable). In this situation, consider instead picking out the target type: +[source, java] .... call(void run()) && target(Service) .... @@ -1058,6 +937,7 @@ method signature specifies a declaring type, the pointcut will only match methods declared in that type, or methods that override methods declared in or inherited by that type. So the pointcut +[source, java] .... execution(public void Middle.*()) .... @@ -1068,6 +948,7 @@ Middle, even if those methods are overridden in a subclass of Middle. So the pointcut would pick out the method-execution join point for Sub.m() in this code: +[source, java] .... class Super { protected void m() { ... } @@ -1085,6 +966,7 @@ Type patterns may be used to pick out methods and constructors based on their throws clauses. This allows the following two kinds of extremely wildcarded pointcuts: +[source, java] .... pointcut throwsMathlike(): // each call to a method with a throws clause containing at least @@ -1190,6 +1072,7 @@ So exact type patterns match based on usual Java scope rules. There is a special type name, *, which is also a type pattern. * picks out all types, including primitive types. So +[source, java] .... call(void foo(*)) .... @@ -1202,6 +1085,7 @@ patterns. The * wildcard matches zero or more characters characters except for ".", so it can be used when types have a certain naming convention. So +[source, java] .... handler(java.util.*Map) .... @@ -1209,6 +1093,7 @@ handler(java.util.*Map) picks out the types java.util.Map and java.util.java.util.HashMap, among others, and +[source, java] .... handler(java.util.*) .... @@ -1221,6 +1106,7 @@ The "`..`" wildcard matches any sequence of characters that start and end with a ".", so it can be used to pick out all types in any subpackage, or all inner types. So +[source, java] .... within(com.xerox..*) .... @@ -1238,6 +1124,7 @@ It is possible to pick out all subtypes of a type (or a collection of types) with the "+" wildcard. The "+" wildcard follows immediately a type name pattern. So, while +[source, java] .... call(Foo.new()) .... @@ -1245,6 +1132,7 @@ call(Foo.new()) picks out all constructor call join points where an instance of exactly type Foo is constructed, +[source, java] .... call(Foo+.new()) .... @@ -1252,6 +1140,7 @@ call(Foo+.new()) picks out all constructor call join points where an instance of any subtype of Foo (including Foo itself) is constructed, and the unlikely +[source, java] .... call(*Handler+.new()) .... @@ -1271,6 +1160,7 @@ Type patterns are built up out of type name patterns, subtype patterns, and array type patterns, and constructed with boolean operators `&&`, `||`, and `!`. So +[source, java] .... staticinitialization(Foo || Bar) .... @@ -1278,6 +1168,7 @@ staticinitialization(Foo || Bar) picks out the static initializer execution join points of either Foo or Bar, and +[source, java] .... call((Foo+ && ! Foo).new(..)) .... @@ -1289,6 +1180,7 @@ not Foo itself, is constructed. Here is a summary of the pattern syntax used in AspectJ: +[source, text] .... MethodPattern = [ModifiersPattern] TypePattern @@ -1397,6 +1289,7 @@ interpretations of after advice: After the execution of a join point completes normally, after it throws an exception, or after it does either one. AspectJ allows after advice for any of these situations. +[source, java] .... aspect A { pointcut publicCall(): call(public Object *(..)); @@ -1415,6 +1308,7 @@ aspect A { After returning advice may not care about its returned object, in which case it may be written +[source, java] .... after() returning: call(public Object *(..)) { System.out.println("Returned normally"); @@ -1448,6 +1342,7 @@ must be declared with a return type, like a method. Thus, a simple use of around advice is to make a particular method constant: +[source, java] .... aspect A { int around(): call(int C.foo()) { @@ -1459,6 +1354,7 @@ aspect A { Within the body of around advice, though, the computation of the original join point can be executed with the special syntax +[source, java] .... proceed( ... ) .... @@ -1468,6 +1364,7 @@ pointcut, and returns whatever the around is declared to return. So the following around advice will double the second argument to `foo` whenever it is called, and then halve its result: +[source, java] .... aspect A { int around(int i): call(int C.foo(Object, int)) && args(i) { @@ -1483,6 +1380,7 @@ is originally a primitive value. And when the advice returns an Object value, that value is converted back to whatever representation it was originally. So another way to write the doubling and halving advice is: +[source, java] .... aspect A { Object around(int i): call(int C.foo(Object, int)) && args(i) { @@ -1500,6 +1398,7 @@ program the first call to proceed will be treated as a method call to the `ICanProceed` instance, whereas the second call to proceed is treated as the special proceed form. +[source, java] .... aspect A { Object around(ICanProceed canProceed) : execution(* *(..)) && this(canProceed) { @@ -1519,6 +1418,7 @@ method parameters. In particular, assigning to any parameter affects only the value of the parameter, not the value that it came from. This means that +[source, java] .... aspect A { after() returning (int i): call(int C.foo()) { @@ -1536,6 +1436,7 @@ less-precedent advice and the underlying join point by supplying different values for the variables. For example, this aspect replaces the string bound to `s` in the named pointcut `privateData`: +[source, java] .... aspect A { Object around(String s): MyPointcuts.privateData(s) { @@ -1551,6 +1452,7 @@ In the following aspect, the around advice replaces the declared target `List` with an `ArrayList`. This is valid code at compile-time since the types match. +[source, java] .... import java.util.*; @@ -1565,6 +1467,7 @@ But imagine a simple program where the actual target is `LinkedList`. In this case, the advice would cause a `ClassCastException` at runtime, and `peek()` is not declared in `ArrayList`. +[source, java] .... public class Test { public static void main(String[] args) { @@ -1577,6 +1480,7 @@ The `ClassCastException` can occur even in situations where it appears to be unnecessary, e.g., if the program is changed to call `size()`, declared in `List`: +[source, java] .... public class Test { public static void main(String[] args) { @@ -1605,6 +1509,7 @@ signalled by the compiler. For example, in the following declarations: +[source, java] .... import java.io.FileNotFoundException; @@ -1626,22 +1531,14 @@ aspect A { both pieces of advice are illegal. The first because the body throws an undeclared checked exception, and the second because field get join -points cannot throw `FileNotFoundException`s. +points cannot throw ``FileNotFoundException``s. The exceptions that each kind of join point in AspectJ may throw are: method call and execution:: - the checked exceptions declared by the target method's - + - throws - + - clause. + the checked exceptions declared by the target method's `_throws_` clause. constructor call and execution:: - the checked exceptions declared by the target constructor's - + - throws - + - clause. + the checked exceptions declared by the target constructor's `_throws_` clause. field get and set:: no checked exceptions can be thrown from these join points. exception handler execution:: @@ -1649,11 +1546,7 @@ exception handler execution:: static initializer execution:: no checked exceptions can be thrown from these join points. pre-initialization and initialization:: - any exception that is in the throws clause of - + - all - + - constructors of the initialized class. + any exception that is in the throws clause of all constructors of the initialized class. advice execution:: any exception that is in the throws clause of the advice. @@ -1700,6 +1593,7 @@ precedence over the one that appears later. These rules can lead to circularity, such as +[source, java] .... aspect A { before(): execution(void main(String[] args)) {} @@ -1747,6 +1641,7 @@ encapsulates some of the context of the advice's current or enclosing join point. These variables exist because some pointcuts may pick out very large collections of join points. For example, the pointcut +[source, java] .... pointcut publicCall(): call(public * *(..)); .... @@ -1843,6 +1738,7 @@ The effect of such a declaration is to make <OnType> support the new method. Even if <OnType> is an interface. Even if the method is neither public nor abstract. So the following is legal AspectJ code: +[source, java] .... interface Iface {} @@ -1979,6 +1875,7 @@ Inter-type declarations raise the possibility of conflicts among locally declared members and inter-type members. For example, assuming `otherPackage` is not the package containing the aspect `A`, the code +[source, java] .... aspect A { private Registry otherPackage.onType.r; @@ -2001,6 +1898,7 @@ there is no conflict: The aspect cannot see such a field, and no code in If `onType` defines a public field "`r`", there is a conflict: The expression +[source, java] .... this.r = r .... @@ -2071,6 +1969,7 @@ might define appropriate inter-type `void fulfills the `Runnable` interface. In order to implement the methods in the `Runnable` interface, the inter-type `run()` method must be public: +[source, java] .... aspect A { declare parents: SomeClass implements Runnable; @@ -2091,9 +1990,10 @@ initializers. The order of super-interface instantiation is observable. We fix this order with the following properties: A supertype is initialized before a subtype, initialized code runs only once, and the initializers for a type's superclass are run before the initializers for -its superinterfaces. Consider the following hierarchy where \{`Object`, -`C`, `D`, `E`} are classes, \{`M`, `N`, `O`, `P`, `Q`} are interfaces. +its superinterfaces. Consider the following hierarchy where {`Object`, +`C`, `D`, `E`} are classes, {`M`, `N`, `O`, `P`, `Q`} are interfaces. +[source, text] .... Object M O \ / \ / @@ -2106,6 +2006,7 @@ its superinterfaces. Consider the following hierarchy where \{`Object`, when a new `E` is instantiated, the initializers run in this order: +[source, text] .... Object M C O N D Q P E .... @@ -2158,6 +2059,7 @@ Pointcut For example, the aspect +[source, java] .... aspect A { declare soft: Exception: execution(void main(String[] args)); @@ -2169,6 +2071,7 @@ Would, at the execution join point, catch any `Exception` and rethrow a This is similar to what the following advice would do +[source, java] .... aspect A { void around() execution(void main(String[] args)) { @@ -2187,6 +2090,7 @@ Like advice, the declare soft form has no effect in an abstract aspect that is not extended by a concreate aspect. So the following code will not compile unless it is compiled with an extending concrete aspect: +[source, java] .... abstract aspect A { abstract pointcut softeningPC(); @@ -2223,6 +2127,7 @@ of their name should have precedence over all other aspects, and (2) the Logging aspect (and any aspect that extends it) should have precedence over all non-security aspects, can be expressed by: +[source, java] .... declare precedence: *..*Security*, Logging+, *; .... @@ -2235,6 +2140,7 @@ accomplished by stating that CountEntry has precedence over DisallowNulls. This declaration could be in either aspect, or in another, ordering aspect: +[source, java] .... aspect Ordering { declare precedence: CountEntry, DisallowNulls; @@ -2259,6 +2165,7 @@ aspect CountEntry { It is an error for any aspect to be matched by more than one TypePattern in a single decare precedence, so: +[source, java] .... declare precedence: A, B, A ; // error .... @@ -2267,6 +2174,7 @@ However, multiple declare precedence forms may legally have this kind of circularity. For example, each of these declare precedence is perfectly legal: +[source, java] .... declare precedence: B, A; declare precedence: A, B; @@ -2280,6 +2188,7 @@ idiom that can be used to enforce that A and B are strongly independent. Consider the following library aspects: +[source, java] .... abstract aspect Logging { abstract pointcut logged(); @@ -2313,12 +2222,14 @@ aspects, say, MyLogging and MyProfiling. Because advice only applies from concrete aspects, the declare precedence form only matters when declaring precedence with concrete aspects. So +[source, java] .... declare precedence: Logging, Profiling; .... has no effect, but both +[source, java] .... declare precedence: MyLogging, MyProfiling; declare precedence: Logging+, Profiling+; @@ -2526,6 +2437,7 @@ All advice runs in the context of an aspect instance, but it is possible to write a piece of advice with a pointcut that picks out a join point that must occur before asopect instantiation. For example: +[source, java] .... public class Client { @@ -2574,6 +2486,7 @@ private or protected resources of other types. To allow this, aspects may be declared `privileged`. Code in priviliged aspects has access to all members, even private ones. +[source, java] .... class C { private int i = 0; @@ -2594,6 +2507,7 @@ If a privileged aspect can access multiple versions of a particular member, then those that it could see if it were not privileged take precedence. For example, in the code +[source, java] .... class C { private int i = 0; |