| <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> |
| <html> <head> |
| <title>AspectJ Tutorial Exercises</title> |
| </head> |
| |
| <body> |
| <h1>AspectJ Tutorial Exercises</h1> |
| |
| <h3>Organization</h3> |
| |
| <p> The exercises work with a figure editor together with JUnit test |
| cases. They progress, as most users do in their adoption of AspectJ, |
| from non-functional, development-only aspects to aspects which augment |
| a deployed program with crosscutting features. </p> |
| |
| <p> For each exercise, you will compile and run with the AspectJ |
| runtime and the JUnit test cases. The JUnit tests verify your code by |
| inspecting messages you send using the <code>Log</code> class which is |
| in the <code>support</code> directory. </p> |
| |
| <p> If you are using Eclipse, compiling your project is accomplished |
| with the compile button to the left of the perspective pull-down menu |
| in the toolbar. If you are using the command-line tools, then you |
| should compile all the files in base.lst, as well as whatever aspect |
| code you add for an exercise (<code>figures/Answer1a.java</code>, in |
| this example): </p> |
| |
| <blockquote><PRE> |
| $ ajc -Xlint -argfile base.lst figures/Answer1a.java |
| </PRE> </blockquote> |
| |
| <p> If you are using Eclipse, running one of the unit tests is |
| accomplished by right clicking the test (in the <code>tests</code> |
| package) and selecting "Run". If you are using the command-line |
| tools, then just invoke java on the compiled test class, such as </p> |
| |
| <blockquote><PRE> |
| $ java tests.Test |
| </PRE> </blockquote> |
| |
| <p> The default test, <code>tests.Test</code>, performs some |
| rudimentary tests on figure elements, and so is a useful test to |
| run periodically. |
| </p> |
| |
| <p> To complete each exercise, create a file containing your aspects |
| and include the file in your compile (e.g., replacing |
| figures/Answer1a.java above). Use the AspectJ documentation as a |
| reference, particularly the quick-reference and semantics appendices |
| of the programming guide. The compiler's <code>-Xlint</code> option |
| may catch some common mistakes. If you get stuck, inspect the test |
| case included for each exercise. </p> |
| |
| <hr> |
| <!-- page break --> |
| <h2>1. Log simple properties</h2> |
| |
| <h3>a. Trace one method</h3> |
| |
| <p> Intstrument your code so that before the Point's setX method |
| begins executing, you log the string "set". To do this, use the |
| utility class <code>Log</code> (with an import from |
| <code>support.Log</code>) and call </p> |
| |
| <blockquote><PRE> |
| Log.log("set") |
| </PRE></blockquote> |
| |
| <p> This will write the string "set", followed by a semicolon |
| terminator, to the Log. For example, with your aspect enabled, |
| </p> |
| |
| <blockquote><PRE> |
| Point p1 = new Point(10, 100); |
| p1.setX(37); |
| System.out.println(Log.getString()); |
| </PRE></blockquote> |
| |
| <p> should print out "set;". |
| </p> |
| |
| <p> Test this with the JUnit test case <code>tests.Test1a</code>. |
| Without adding any aspects, this test should fail: </p> |
| |
| <blockquote><PRE> |
| $ ajc -argfile base.lst |
| $ java tests.Test1a |
| ..F....... |
| Time: 0.07 |
| There was 1 failure: |
| 1) testSetXPointLog(tests.Test1a)junit.framework.AssertionFailedError: expected:<set;> but was:<> |
| at tests.Test1a.testSetXPointLog(Test1a.java:30) |
| at tests.Test1a.main(Test1a.java:16) |
| |
| FAILURES!!! |
| Tests run: 9, Failures: 1, Errors: 0 |
| </PRE></blockquote> |
| |
| <p> But with the proper aspect added to the compilation, (in this |
| case, <code>figures/Answer1a.java</code>, but you should feel free to use |
| more evocative names), the test should pass </p> |
| |
| <blockquote><PRE> |
| $ ajc -argfile base.lst figures/Answer1a.java |
| $ java tests.Test1a |
| ......... |
| Time: 0.089 |
| |
| OK (9 tests) |
| </PRE></blockquote> |
| |
| <p> Make sure to pass <code>tests.Test1a</code> before |
| continuing. |
| </p> |
| |
| <h3>b. Trace multiple methods </h3> |
| |
| <p> Continue your aspect to capture execution of more methods. |
| </p> |
| |
| <p> When <code>Point.setX</code> or <code>Point.setY</code> execute, |
| you should log "set". When one of |
| </p> |
| |
| <blockquote><pre> |
| Point.getX, Point.getY, Line.getP1, Line.getP2 |
| </pre></blockquote> |
| |
| <p> execute, you should log "get". |
| </p> |
| |
| <p> Feel free to use wildcards in your pointcuts. |
| </p> |
| |
| <p> For example, |
| </p> |
| |
| <blockquote><PRE> |
| Point p1 = new Point(10, 100); |
| p1.setY(p1.getX()); |
| System.out.println(Log.getString()); |
| </PRE></blockquote> |
| |
| |
| <p> should print out "get;set;". |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test1b</code> |
| before continuing. </p> |
| |
| |
| <h3>c. Use join point information</h3> |
| |
| <p> Instead of logging <code>"set"</code> and <code>"get"</code>, log |
| the name of the executing method. For example, |
| </p> |
| |
| <blockquote><pre> |
| Point p1 = new Point(10, 100); |
| p1.setY(p1.getX()); |
| System.out.println(Log.getString()); |
| </pre></blockquote> |
| |
| <p> would print out "getX;setY;". |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test1c</code> |
| before continuing. </p> |
| |
| <h3>d. Refactor and extend</h3> |
| |
| <p> At this point, check the people to your left and right. If |
| they're stuck somewhere, see if you can help them. If the people to |
| your left and right are doing fine, then think about refactoring your |
| logging aspect from part c. You could separate your logging aspect |
| into two aspects, one encapsulating the logging protocol, and the |
| other encapsulating the choice of join points. See if there are more |
| interesting refactorings to do. </p> |
| |
| <p> In your refactoring, you make sure you continue to pass the JUnit |
| test <code>tests.Test1c</code>. </p> |
| |
| <hr> |
| |
| <h2>2. More advanced Tracing</h2> |
| |
| <p> If you did some refactoring in the last exercise, restore your |
| packages to their original contents (which were saved in the |
| <code>backup</code> directory wherever you unpacked |
| <code>figures.zip</code>) before starting the next exercises. </p> |
| |
| <h3>a. Tracing external calls</h3> |
| |
| <p> It can often be useful to trace only those calls that are made to |
| outside code. Write an aspect to log calls to methods in the |
| <code>java.util</code> package from the <code>figures</code> package. |
| </p> |
| |
| <p> You should send a short description of the join point to the log, |
| with |
| </p> |
| |
| <blockquote><pre> |
| Log.log(thisJoinPointStaticPart.getSignature().toShortString()); |
| </pre></blockquote> |
| |
| <p> For example, if you compiled your aspect with this |
| <code>Main</code> class </p> |
| |
| <blockquote><pre> |
| package figures; |
| |
| class Main { |
| public static void Main(String[] args) { |
| java.util.Set s = new java.util.HashSet(); |
| s.add(null); |
| s.clear(); |
| System.out.println(Log.getString()); |
| } |
| } |
| </pre></blockquote> |
| |
| <p> running it would print out "Set.add(Object);Set.clear();". |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test2a</code> |
| before continuing. </p> |
| |
| <h3>b. Logging exceptions thrown</h3> |
| |
| <p> Instead of logging whenever we make an external call to method |
| from <code>java.util</code>, modify your code to log any |
| <code>Throwable</code> thrown from such a call from code in the |
| <code>figures</code> and <code>test</code> packages. </p> |
| |
| <p> For example, if you compiled your aspect with this |
| <code>Main</code> class </p> |
| |
| <blockquote><pre> |
| package figures; |
| |
| class Main { |
| public static void Main(String[] args) { |
| java.util.Vector v = new java.util.Vector(); |
| try { |
| v.get(1); |
| } catch (ArrayIndexOutOfBoundsException e) { } |
| System.out.println(Log.getString()); |
| } |
| } |
| </pre></blockquote> |
| |
| <p> running it would print out <code>"caught |
| java.lang.ArrayIndexOutOfBoundsException;"</code>. </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test2b</code> |
| before continuing. </p> |
| |
| <h3>c. Exposing caller and callee</h3> |
| |
| <p> As an extremely artifical example leading up to the next exercise, |
| write an aspect that writes to the log: |
| </p> |
| |
| <ul> |
| <li>only when a Group calls move on a Box</li> |
| </ul> |
| |
| <p> Log the size of the calling group and the p0 point of the target |
| box. |
| </p> |
| |
| <p> For example, if you compiled your aspect with this |
| <code>Main</code> class </p> |
| |
| <blockquote><pre> |
| package figures; |
| |
| class Main { |
| public static void Main(String[] args) { |
| Group g = new Group(new Point(30, 70)); |
| Box b = new Box(5, 5, 10, 10); |
| g.add(b); |
| g.move(100, 100); // logs "2Point(5, 5)" |
| g.add(new Point(1, 99)); |
| g.move(-2, -10); // logs "3Point(105, 105)" |
| System.out.println(Log.getString()); |
| } |
| } |
| </pre></blockquote> |
| |
| <p> running it would print out |
| </p> |
| |
| <blockquote><pre> |
| 2Point(5, 5);3Point(105,105); |
| </pre></blockquote> |
| |
| <p> As you may already have noticed, looking at the included JUnit |
| tests will also help with the problems. </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test2c</code> |
| before continuing. </p> |
| |
| <h3>d. Tracing only in a control flow</h3> |
| |
| <p> Write an aspect that logs when the <code>_x</code> and |
| <code>_y</code> fields on <code>Point</code> are set, but only when in |
| the process of moving a <code>Box</code>. </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test2d</code> |
| before continuing. </p> |
| |
| <h3>e. Enabling per-object tracing</h3> |
| |
| <p> Write an aspect that allows the specification of individual figure |
| elements whose move method should be logged. This will involve |
| associating a boolean value with every figure element. Use around |
| advice to intercept calls to <code>Log.traceObject(Object)</code> |
| (which otherwise just signals an exception) to register the passed |
| object as traceable. Then, write advice that logs the class name of |
| any such moved object, but only if the object is registered as being |
| traced. You may wish to use the <code>if</code> pointcut.</p> |
| |
| <p> The Log class has a method that will log the short name of a Class |
| minus its package name. Since this is the form expected by the test |
| case, you should probably use <code>support.Log.logClassName(Class)</code>. |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test2e</code> |
| before continuing. </p> |
| |
| |
| <h3>f. Refactor and extend</h3> |
| |
| <p> At this point, check the people to your left and right. If |
| they're stuck somewhere, see if you can help them. If the people to |
| your left and right are doing fine, then think about refactoring any |
| of the aspects or the system in general, perhaps starting by merging |
| part d and e. </p> |
| |
| <p> In your refactoring, make sure you continue to pass appropriate |
| the JUnit tests. </p> |
| |
| <hr> |
| <!-- page break --> |
| <h2>3. Check Invariants</h2> |
| |
| <p> If you did some refactoring in the last exercise, restore your |
| packages to their original contents (which you should be able to do by |
| copying the contents of backup) before starting the next exercises. |
| </p> |
| |
| <h3>a. Check static invariants</h3> |
| |
| <p> One common coding convention is that no private field should be |
| set outside of setter methods or constructors. Write an aspect to |
| warn at compile time when such an illegal assignment expression |
| exists. </p> |
| |
| <p> You will find that the convention is violated twice in the figures |
| package, but you may or may not agree that the "violation" is bad |
| style. Deal with this information, either by making your aspect more |
| nuanced by relaxing the coding convention, or by fixing the code to |
| remove the violation. </p> |
| |
| <p> Make sure your program still passes the JUnit test |
| <code>tests.Test</code> (which it should also pass at the beginning of |
| all exercises) before continuing. </p> |
| |
| |
| <h3>b. Check preconditions</h3> |
| |
| <p> Write an aspect to throw an <code>IllegalArgumentException</code> |
| whenever an attempt is made to set one of <code>Point</code>'s |
| <code>int</code> fields to a value that is either too large (greater |
| than <code>FigureElement.MAX_VALUE</code>) or too small (less than |
| <code>FigureElement.MIN_VALUE</code>). </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test3b</code> |
| before continuing. </p> |
| |
| |
| <h3>c. Assure input</h3> |
| |
| <p> Instead of throwing an exception when one of <code>Point</code>'s |
| <code>int</code> fields are set to an out-of-bounds value, write an |
| aspect to trim the value into an in-bounds one. |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test3c</code> |
| before continuing. </p> |
| |
| |
| <h3>d. Check postconditions</h3> |
| |
| <p> There is a method on the <code>Box</code> class, <code>void |
| checkBoxness()</code>, that checks whether the four points making up a |
| box are correctly positioned relative to each other. Write an aspect |
| that will make sure that after all calls to public methods of Box |
| return, the <code>checkBoxness()</code> method is called. </p> |
| |
| <p> When testing this aspect, you may find yourself facing a |
| <code>StackOverflowException</code>. If so, carefully look at your |
| pointcuts. </p> |
| |
| <p> You may also see an irrecoverable stack overflow occurring, which |
| may either show as such or simply abort silently, depending on your |
| JVM. In this case, you should also check carefully to see what you're |
| doing wrong, but here's a hint: The following program probably has |
| the same behavior: |
| </p> |
| |
| <blockquote><PRE> |
| class Test { |
| public static void main(String[] args) { |
| try { |
| main(args); |
| } finally { |
| main(args); |
| } |
| } |
| } |
| </PRE></blockquote> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test3d</code> |
| before continuing. </p> |
| |
| <h3>e. Check postconditions</h3> |
| |
| <p> <code>FigureElement</code>s support the <code>getBounds()</code> |
| method that returns a <code>java.awt.Rectangle</code> representing the |
| bounds of the object. An important postcondition of the |
| <code>move</code> operation is that the figure element's bounds |
| rectangle should move by the same amount as the figure itself. Write |
| an aspect to check for this postcondition. |
| </p> |
| |
| <p> You can create a copy of a figure element's bounds rectangle with |
| <code>new Rectangle(fe.getBounds())</code>, and you can move a bounds |
| rectangle <code>rect</code> with <code>rect.translate(dx, dy)</code>. |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test2f</code> |
| before continuing. </p> |
| |
| |
| <h3>f. Refactor and extend</h3> |
| |
| <p> At this point, check the people to your left and right. If |
| they're stuck somewhere, see if you can help them. If the people to |
| your left and right are doing fine, then think about refactoring any |
| of the aspects or the system in general. |
| </p> |
| |
| <p> In your refactoring, make sure you continue to pass appropriate |
| the JUnit tests. </p> |
| |
| <hr> |
| <!-- page break --> |
| <h2>4. Add crosscutting functionality</h2> |
| |
| <p> If you did some refactoring in the last exercise, restore your |
| packages to their original contents (which were saved in the |
| <code>backup</code> directory wherever you unpacked |
| <code>figures.zip</code>) before starting the next exercises. </p> |
| |
| <h3>a. Add Colorizing Support</h3> |
| |
| <p> Figure elements currently have a hard-coded color. The first part |
| of crosscutting functionality you will add will be support for |
| figureElements to have settable line and fill colors. We have |
| provided a stub aspect <code>figures.ColorControl</code> for you to |
| fill in; that aspect's static methods are called from the JUnit test |
| <code>tests.Test4a</code>. </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test4a</code> |
| before continuing. </p> |
| |
| |
| <h3>b. Caching group bounds part 1: Manage enclosing groups</h3> |
| |
| <p> The second crosscutting feature you will be adding is support from |
| caching the bound objects of <code>Group</code> figure elements, which |
| may be costly to compute. Before starting that, write a tracing |
| aspect to log all of a point's enclosing group identifiers whenever |
| that point moves. |
| </p> |
| |
| <p> You need only deal with points that are added directly to Groups. |
| You should handle those points contained in Lines and Boxes only if |
| time permits. |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test4b</code> |
| before continuing. </p> |
| |
| <h3>c. Caching group bounds part 2: cache and invalidate</h3> |
| |
| <p> Now that you have a structure in place to maintain information |
| about a point's enclosing groups, cache group's bounds rectangle so |
| that repeated calls to <code>getBounds()</code> returns the same |
| rectangle. However, whenever a point moves it should invalidate the |
| caches of all enclosing groups. |
| </p> |
| |
| <p> Make sure to pass the JUnit test <code>tests.Test4b</code> |
| before continuing. </p> |
| |
| <h3>d. Refactor and Extend</h3> |
| |
| <p> Congratulations! As before, check the people to your left and |
| right and see if they're stuck. Otherwise, do any other refactorings |
| that would make the code more robust or easier to maintain. Consider |
| whether abstracting out parts of the gui-handling code would improve |
| modularity. Bring back some of your tracing aspects from exercise 1 |
| and explore the newly modularized system. </p> |
| |
| |
| <hr> |
| </body> </html> |