]> source.dussan.org Git - aspectj.git/commitdiff
tutorial exercises updated for OOPSLA. The printout
authorehilsdal <ehilsdal>
Mon, 23 Aug 2004 23:08:20 +0000 (23:08 +0000)
committerehilsdal <ehilsdal>
Mon, 23 Aug 2004 23:08:20 +0000 (23:08 +0000)
looks much nicer and is a bit more streamlined.

docs/teaching/exercises/figures_classes.gif
docs/teaching/exercises/index.html

index a5952fb9ebb974df7b5f26bb2c6c6cb93c9190a4..3440bbff828ee63f3f7caa88271e51f9f6b0bdac 100644 (file)
Binary files a/docs/teaching/exercises/figures_classes.gif and b/docs/teaching/exercises/figures_classes.gif differ
index b6130821f0b5f4a4e18a946bc1073010d2f729b5..84b11cd203f2c79a3b2dd560a9b09348b69e6639 100644 (file)
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
-<html> <head>
-<title>AspectJ Exercises</title>
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+
+<!-- This file represents the Exercises for the hands-on AspectJ 
+     tutorial.  It is commonly checked into CVS with identifying 
+     information for the latest conference (such as presenters 
+     and publication information). -->
+
+<!-- When you use it for your own purposes, don't forget to
+     modify at the very least anything that says
+     id="copyright" or class="presenter" -->
+
+<!-- Also, the gif included at the end is somewhat fragile,
+     so be careful with different paper sizes. -->
+
+
+<head>
+  <title>Hands-on Programming with AspectJ &mdash; Exercises</title>
+  <style type="text/css">
+div.instruction { padding: 0.5em; border-width: 1px; border-style: solid }
+body { background-color: #FFF; margin: 2em }
+body { font-family: "Gill Sans MT", "Gill sans", "Trebuchet ms", Verdana, sans-serif; }
+.newpage { page-break-before: always }
+pre { margin-left: 1em; border-left-style: solid; border-width: 1px; padding-left: 1em;}
+h2 { margin-top: 4ex; }
+h3 { margin-top: 4ex; border-bottom-style: solid; border-width: 1px }
+.presenter { text-align: right }
+@page {
+  size: 8.5in 11in;
+  margin: 3in;
+  marks: cross
+}
+@media print {
+  body { font-size: 10pt }
+  #copyright {
+    font-family: "Times New Roman", "Times Roman", fixed;
+    font-size: 8pt;
+    display: normal;
+    position: absolute;
+    bottom: 1in;
+    border-style: none
+  }
+}
+@media screen {
+  #copyright { display: none }
+}
+  </style>
 </head>
 
-<body bgcolor="white">
-<h2>Hands-on Aspect-Oriented Programming with AspectJ</h2>
+<body>
+
+<h1>Hands-on Programming with AspectJ</h1>
+
+<div class="presenter">Erik Hilsdale</div>
+<div class="presenter">Mik Kersten</div>
+<div class="presenter">http://www.eclipse.com/aspectj</div>
+
+<h2>Overview</h2>
 
-<h3>Organization</h3>
+<p> In this tutorial you will solve some canonical programming
+tasks using AspectJ.  The tasks progress from writing
+non-functional, development-only aspects to writing aspects that
+augment a deployed program with crosscutting features.  This
+follows the same progression most users see in their own adoption
+of AspectJ. </p>
 
-<p> This tutorial consists of the attendees solving AspectJ
-programming tasks (with unit tests) in pairs or triples and discussing
-the answers with the group and with the tutorial presenters.  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> Since this is a hands-on tutorial, you will be working with a
+live AspectJ distribution.  The example code we will be working
+with is a simple figure editor, along with JUnit tests for each
+exercise.  We will break up into groups of two to three people
+per computer to foster discussion within the group as well as
+with the presenters.  </p>
+
+<p> If you have a laptop running a recent version of Windows,
+MacOS or Linux, feel free to bring it along.  We will provide CDs
+and other installation media for a standalone AspectJ system,
+including the figure editor code these exercises are based on and
+unit tests for the exercises.  If you don't have a laptop with
+you, don't worry about it.  </p>
 
 <p> These notes consist of four sections of exercises, a quick
 reference to AspectJ syntax, and a UML diagram of a figure editor
-program.  While you should feel free to have a quick look through
-these notes before the tutorial, please do not try to seriously read
-or do the exercises; you'll learn a lot more by working through it in
-groups. </p>
-
-<h3>Command-line usage</h3>
-
-<p> At the beginning of the tutorial we will make available a binary
-package that includes the tests, the base code, JUnit, and a
-distribution of AspectJ.  All it needs is information about where Java
-lives (so set your JAVA_HOME environment variable).  It assumes that
-you unzip it in c:\ (on Windows) or in your home directory (on Linux):
-If you put it somewhere else, edit setpaths or setpaths.bat, as
-appropriate.  Once all this is done, run <code>setpaths.bat</code> or
-<code>source setpaths</code> to export some other needed environment
-variables. </p>
-
-<p> All the files in the program are listed in base.lst, including
-test cases and an empty answer aspect,
-<code>answers/Answer.java</code>.  Therefore, if you write your
-answers there, all you need to do is compile base.lst, either in an
-IDE or with </p>
-
-<blockquote><PRE>
+program. </p>
+
+<div class="instruction"> If you receive these tutorial notes
+early, feel free to have a quick look, especially at the UML
+diagram and quick reference.  But you'll be cheating yourself if
+you try to do the exercises early; you'll learn a lot more by
+working through it in groups during the tutorial proper.  </div>
+
+<div id="copyright">
+Copyright is held by the author/owner(s). <br />
+OOPSLA\9204, October 24-28, 2004, Vancouver, British Columbia, Canada <br />
+2004 ACM 04/0010
+</div>
+
+<h3 class="newpage">Command-line usage</h3>
+
+<p> While the AspectJ system is well integrated with a number of
+IDEs, it can also be used as a command-line compiler.  The
+standalone package we provide (containing the tests, the base
+code, JUnit, and a distribution of AspectJ) needs information
+about where Java lives (so set your JAVA_HOME environment
+variable).  It assumes that you unzip it in c:\ (on Windows) or
+in your home directory (on Linux): If you put it somewhere else,
+edit <code>setpaths</code> or <code>setpaths.bat</code>, as
+appropriate.
+</p>
+
+<p> Each time you open a new shell window run
+<code>setpaths.bat</code> or <code>source setpaths</code> to
+export some other needed environment variables. </p>
+
+<p> In general, all the files in the program are listed in
+<code>base.lst</code>, including test cases and an empty answer
+aspect, <code>answers/Answer.java</code>.  Therefore, if you
+write your answers there, all you need to do is compile
+<code>base.lst</code>, either in an IDE or with </p>
+
+<pre>
 $ ajc -argfile base.lst
-</PRE></blockquote>
+</pre>
 
 <p> Before you move onto another exercise, though, make sure to copy
 your answer into a different file so we can discuss the answers
 together:
 </p>
 
-<blockquote><PRE>
+<pre>
 &gt; copy answers/Answer.java answers/2a.java  (Windows)
 $ cp answers/Answer.java answers/2a.java    (Unix)
-</PRE></blockquote>
+</pre>
 
 <p> After building the system, you should invoke Java on the compiled
 test class.  On the command-line, this this would be </p>
 
-<blockquote><PRE>
+<pre>
 $ java tests.Test2a
-</PRE> </blockquote>
-
-<p> (For these exercises, when we give examples of execution we will
-show the command-line use, but of course if you are using JBuilder,
-Forte/NetBeans, Emacs, or Eclipse, use the appropriate compile and
-execute tools.)  </p>
+</pre> 
 
 <p> The default test, <code>tests.Test</code>, performs some
 rudimentary tests on figure elements, and so is a useful test to run
-periodically.  Looking at the JUnit tests for each exercise may also
-be helpful. </p>
-
-<p> Again, we will be looking at some solutions and having discussion,
-which is much more difficult without incremental solutions.  So when
-you go from one exercise to the next, make sure to save your work in a
-file and go on to work in a different file, even if you plan to
-duplicate some code.  </p>
-
-<h3>Environment</h3>
-
-<p> You may use whatever editor or environment you choose to work
-through these exercises.  We provide a simple code-browser that can
-work well as an editor for these short exercises, in addition to
-providing better visualization of how aspects affect the system: </p>
-
-<blockquote><PRE>
-$ ajbrowser base.lst
-</PRE></blockquote>
-
-<p> With the browser you can edit code (including the
-<code>answers/Answer.java</code> file), and after saving hit the build
-button to start an ajc compile.  We recommend you start up another
-shell, though, to run the JUnit tests (and don't forget to run the
-<code>setpaths</code> script when you open the new shell):  You could
-set up the run button to run a test through the Options menu, but
-we've found this is fairly cumbersome. </p>
+periodically.  You should also look at the JUnit tests for each
+exercise as you do it. </p>
+
+<p> Again, we will be looking at solutions and having discussion,
+which is much more difficult without incremental solutions.  So
+when you go from one exercise to the next, <strong>save your
+work</strong> in a file before going on to the next exercise
+even if you plan to duplicate some code.  </p>
+
+<div class="instruction"> When we give examples of execution in
+these exercises we will show the command-line use, but of course
+you should use the appropriate compile and execute tools if you
+are using the AspectJ browser, Emacs, or Eclipse.  </div>
 
 <!-- ============================== -->
 
-<br style="page-break-after: always" />
-<h2>1. Static Invariants</h2>
+<h2 class="newpage">1. Static Invariants</h2>
 
 <p> The easiest way to get started with AspectJ is to use it to
 enforce static invariants. 
 </p>
 
-<h3>a. Catch old tracing</h3>
+<h3>1.a. Find old tracing</h3>
 
-<p> <strong>Sample Exercise</strong>: The main point of this exercise
-is to make sure your configuration works.  Type in the answer below
-into your answer file, make sure you get the desired compile-time
-error, remove the offending line, and make sure you pass the JUnit
-test.  </p>
+<div class="instruction"> <strong>Sample Exercise</strong>: The
+main point of this exercise is to make sure your configuration
+works.  Type in the answer below into your answer file, make sure
+you get the desired compile-time error, remove the offending
+line, and make sure you pass the JUnit test.  </div>
 
 <p> <strong>Task:</strong> Signal an error for calls to
 <code>System.out.println</code>. 
@@ -121,70 +179,69 @@ test.  </p>
 <p> The way that we are all taught to print "hello world" from Java is
 to use <code>System.out.println()</code>, so that is what we typically
 use for one-off debugging traces.  It's a common mistake to leave
-these in your system longer than is necessary.  Type in the aspect
-below to force an error at compile time if this mistake is made.
+these in your system far longer than is necessary.  Type in the aspect
+below to signal an error at compile time if this mistake is made.
 </p>
 
+<p> <strong>Answer:</strong>
+</p>
+
+<pre>
+package answers;
+
+import figures.*;
+
+aspect Answer1a {
+    declare error
+        : get(java.io.PrintStream System.out) &amp;&amp; within(figures..*)
+        : "illegal access to System.out";
+}
+</pre>
+
 <p> When you use this on the given system, you'll find one incorrect
 trace in <code>SlothfulPoint</code>.  
 </p>
 
-<blockquote><PRE>
+<pre>
 $ ajc -argfile base.lst
 ./figures/SlothfulPoint.java:38 illegal access to System.out
 
 1 error
-</PRE></blockquote>
+</pre>
 
-<p> Remove the illegal tracing call. 
-</p>
+<p> Note that this answer does not say that the <em>call</em> to the
+<code>println()</code> method is incorrect, rather, that the field get
+of the <code>out</code> field is illegal.  This will also catch those
+users who bind System.out to a static field to save typing.  </p>
+
+<p> After you have successfully used this aspect, edit your
+program to remove the illegal tracing call.  </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>
 
-<blockquote><PRE>
+<pre>
 $ java tests.Test
 ....
 Time: 0.03
 
 OK (4 tests)
-</PRE></blockquote>
-
-<p> <strong>Answer:</strong>
-</p>
-
-<blockquote><PRE>
-package answers;
-
-import figures.*;
-
-aspect Answer1a {
-    declare error
-        : get(java.io.PrintStream System.out) &amp;&amp; within(figures..*)
-        : "illegal access to System.out";
-}
-</PRE></blockquote>
-
-<p> Note that this answer does not say that the <em>call</em> to the
-<code>println()</code> method is incorrect, rather, that the field get
-of the <code>out</code> field is illegal.  This will also catch those
-users who bind System.out to a static field to save typing.  </p>
-
-<br style="page-break-after: always" />
+</pre>
 
-<h3>b. Mandate setters</h3>
+<h3 class="newpage">1.b. Mandate setters</h3>
 
 <p> <strong>Task:</strong> Signal a warning for assignments outside
 of setter methods.  </p>
 
 <p> <strong>Tools:</strong> <code>set</code>, <code>withincode</code>,
-signature <code>void set*(..)</code>
+the <code>void set*(..)</code> pattern
 </p>
 
-<p> One common coding convention is that no private field should be
-set outside of setter methods.  Write an aspect to warn at compile
-time when such an illegal assignment expression exists.  </p>
+<p> One common coding convention is that no private field should
+be assigned to outside of setter methods.  Write an aspect to
+signal a warning at compile time for these illegal assignment
+expressions.  </p>
 
 <p> This is going to look like
 </p>
@@ -200,19 +257,19 @@ outside of setter methods.  "Outside", here, means that the code for
 the assignment is outside the <em>text</em> of the setter. 
 
 <p> Make sure your program still passes the JUnit test
-<code>tests.Test</code> before continuing.  Make sure you get 11
+<code>tests.Test</code> before continuing.  Make sure you get eleven
 warnings from this.  Wait to fix them until the next exercise.  </p>
 
-<h3>c. Refine setters mandate</h3>
+<h3>1.c. Refine setters mandate</h3>
 
 <p> <strong>Task:</strong> Allow assignmnents inside of constructors.
 </p>
 
-<p> <strong>Tools:</strong> signature <code>new(..)</code> </p>
+<p> <strong>Tools:</strong> the <code>new(..)</code> pattern</p>
 
 <p> Look at some of the warnings from the previous exercise.  Notice
 that a lot of them are from within constructors.  Actually, the common
-coding convention is that no private field should be set outside of
+coding convention is that no private field should be assigned to outside of
 setter methods <em>or constructors</em>.  Modify your answer to signal
 an actual error at compile time (rather than just a warning) when such
 an illegal assignment expression exists.  </p>
@@ -225,42 +282,43 @@ pointcut to deal with the constructors.
 the convention is violated twice in the figures package.  You should see
 the following two errors:</p>
 
-<PRE>
+<pre>
 .\figures\Point.java:37 bad field set
 .\figures\Point.java:38 bad field set
 
 2 errors
-</PRE>
+</pre>
 
 <p>Rewrite these two occurrences so as not to violate
 the convention.  Make sure your program still passes the JUnit test
 <code>tests.Test</code> before continuing.  </p>
 
-<h3>d. Congratulations</h3>
-
-<p> You've taken your first steps.  At this point, check the people to
-your left and right.  If they're stuck somewhere, see if you can help
-them.  </p>
+<div class="instruction"> Congratulations, you've taken your
+first steps.  At this point, check the people to your left and
+right.  If they're stuck somewhere, see if you can help them.
+Try to resist moving on to the next section until we discuss
+solutions as a group. </div>
 
 <!-- ============================== -->
 
-<br style="page-break-after: always" />
-<h2>2. Dynamic invariants</h2>
+<h2 class="newpage">2. Dynamic invariants</h2>
 
 <p> The next step in AspectJ adoption is often to augment a test suite
 by including additional dynamic tests.
 </p>
 
-<p> Tutorial attendees typically progress at different speeds through
-these questions.  Throughout this tutorial, if you finish early, see
-what the people around you are doing and if they need help.  Feel free
-to help them out of naked self-interest; we promise you'll learn a lot
-about AspectJ by explaining it. </p>
+<div class="instruction"> Tutorial attendees typically progress
+at different speeds through these exercises.  Throughout this
+tutorial, if you finish early, see what the people around you are
+doing and if they need help.  Don't help them out of charity,
+help them out of naked self-interest&mdash;we promise you'll learn a
+lot about using AspectJ by explaining it. </div>
 
-<h3>a. Check a simple precondition</h3>
+<h3>2.a. Check a simple precondition</h3>
 
-<p> <strong>Sample Exercise</strong>: We've provided the answer to
-this exercise below to get you started.  </p>
+<div class="instruction"> <strong>Sample Exercise</strong>: We've
+provided the answer to this exercise to get you started.  Feel
+free to think a bit, but don't get stuck on this one.  </div>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test2a</code>.
 </p>
@@ -277,7 +335,7 @@ which wouldn't without your aspect.  So before compiling in the
 aspect, 
 </p>
 
-<blockquote><PRE>
+<pre>
 $ java tests.Test2a
 .F..F....
 Time: 0.04
@@ -287,12 +345,12 @@ There were 2 failures:
 
 FAILURES!!!
 Tests run: 7,  Failures: 2,  Errors: 0
-</PRE></blockquote>
+</pre>
 
 <p> But after compiling in the aspect...
 </p>
 
-<blockquote><PRE>
+<pre>
 $ ajc -argfile base.lst answers/Answer.java
 
 $ java tests.Test2a
@@ -300,12 +358,12 @@ $ java tests.Test2a
 Time: 0.04
 
 OK (7 tests)
-</PRE></blockquote>
+</pre>
 
 <p> <strong>Answer:</strong>  
 </p>
 
-<blockquote><PRE>
+<pre>
 package answers;
 
 import figures.*;
@@ -317,9 +375,9 @@ aspect Answer2a {
         } 
     }
 }
-</PRE></blockquote>
+</pre>
 
-<h3>b. Check another precondition</h3>
+<h3>2.b. Check another precondition</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test2b</code>. </p>
 
@@ -336,7 +394,7 @@ value. </p>
 <p> Look at <code>tests/Test2b.java</code> to see exactly what we're
 testing for. </p>
 
-<h3>c. Check yet another precondition</h3>
+<h3>2.c. Check yet another precondition</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test2c</code>. </p>
 
@@ -354,7 +412,7 @@ an attempt is made to call <code>Group.add()</code> on a
 call. 
 </p>
 
-<h3>d. Assure input</h3>
+<h3>2.d. Assure input</h3>
 
 <p> <strong>Task: </strong> Pass <code>tests.Test2d</code>.
 </p>
@@ -381,8 +439,7 @@ aspect A {
 }
 </pre>
 
-<br style="page-break-after: always" />
-<h3>e. Check a postcondition</h3>
+<h3 class="newpage">2.e. Check a postcondition</h3>
 
 <p> <strong>Task: </strong>  Pass <code>tests.Test2e</code>
 </p>
@@ -401,7 +458,7 @@ offset, then the point is in an illegal state and so an
 during move, we need some way of getting access to the coordinates
 both before <em>and</em> after the move, in one piece of advice.  </p>
 
-<h3>f. Check another postcondition</h3>
+<h3>2.f. Check another postcondition</h3>
 
 <p> <strong>Task: </strong>  Pass <code>tests.Test2f</code>
 </p>
@@ -420,14 +477,13 @@ throw an <code>IllegalStateException</code> if it is violated.  </p>
 
 <!-- ============================== -->
 
-<br style="page-break-after: always" />
-<h2>3. Tracing</h2>
+<h2 class="newpage">3. Tracing</h2>
 
 <p> Tracing is one of the classic AspectJ applications, and is often
 the first where AspectJ is used on deployed code.
 </p>
 
-<h3>a. Simple logging</h3>
+<h3>3.a. Simple logging</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test3a</code>.</p>
 
@@ -441,7 +497,7 @@ in the figures package.  To do this, use the utility class
 <code>Log</code> (with an import from <code>support.Log</code>) 
 and call <code>Log.log(String)</code></p>
 
-<h3>b. Exposing a value</h3>
+<h3>3.b. Exposing a value</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test3b</code>.</p>
 
@@ -453,12 +509,12 @@ In this exercise, you will print not only the join point information,
 but also the target object, with the form
 </p>
 
-<blockquote><pre>
+<pre>
 <em>thisJoinPointInfo</em> at <em>targetObject</em>
-</pre></blockquote>
+</pre>
 
 
-<h3>c. More specialized logging</h3>
+<h3>3.c. More specialized logging</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test3c</code>.</p>
 
@@ -469,10 +525,10 @@ but also the target object, with the form
 group. The <code>args</code> pointcut allows you to select join points
 based on the type of a parameter to a method call. </p>
 
-<p> Look at the test case for details about the tested log message.
+<p> Look at the test case for details about the required log message.
 </p>
 
-<h3>d. Logging extended to checking an invariant</h3>
+<h3 class="newpage">3.d. Logging extended to checking an invariant</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test3d</code>.</p>
 
@@ -483,43 +539,39 @@ based on the type of a parameter to a method call. </p>
 To do so, associate a boolean flag with each Point using an inter-type
 declaration, such as </p>
 
-<blockquote><pre>
+<pre>
 boolean Point.hasBeenAdded = false;
-</pre></blockquote>
+</pre>
 
 <p> Check and set this flag with the same kind of advice from your
 answer to problem (c).  Throw an <code>IllegalStateException</code> if
 the point has already been added.
 </p>
 
-<h3>e. Better error messages for 3d</h3>
+<h3>3.e. Better error messages for 3.d.</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test3e</code>.</p>
 
-<p> <strong>Tools:</strong>
-</p>
-
 <p> Extend your solution to problem (d) by using the string
 representation of the Point's containing group as the <code>msg</code>
 part of the <code>IllegalStateException</code>. </p>
 
 <!-- ============================== -->
 
-<br style="page-break-after: always" />
-<h2>4.  Caching</h2>
+<h2 class="newpage">4.  Caching</h2>
 
 <p> Computation of the bounding box of <code>Group</code> objects
 needs to deal with all aggregate parts of the group, and this
 computation can be expensive.  In this section, we will explore
 various ways of reducing this expense. </p>
 
-<p> <strong>Optional</strong>: In all of these exercises, you should
-only deal with points that are added directly to Groups, rather than
-those that are added "indirectly" through Lines and Boxes.  You should
-handle those points contained in Lines and Boxes only if time permits.
-</p>
+<div class="instruction"> <strong>Optional</strong>: In all of
+these exercises, you should only deal with points that are added
+directly to Groups, rather than those that are added "indirectly"
+through Lines and Boxes.  You should handle those points
+contained in Lines and Boxes only if time permits.  </div>
 
-<h3>a. Make a constant override</h3>
+<h3>4.a. Make a constant override</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test4a</code>.</p>
 
@@ -540,7 +592,7 @@ by the static method <code>FigureElement.MAX_BOUNDS</code>.
 around advice intercepting the method.  
 </p>
 
-<h3>b. Make a constant cache</h3>
+<h3>4.b. Make a constant cache</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test4b</code>.
 </p>
@@ -559,7 +611,7 @@ call. </p>
 state for every <code>Group</code> object.</em> </p>
 
 
-<h3>c. Invalidate, part 1</h3>
+<h3>4.c. Invalidate, part 1</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test4c</code>.
 </p>
@@ -573,8 +625,7 @@ Change your aspect so that it invalidates the cache whenever the
 <code>move()</code> method of <code>Group</code> is called. 
 </p>
 
-<br style="page-break-after: always" />
-<h3>d. Invalidate, part 2</h3>
+<h3 class="newpage">4.d. Invalidate, part 2</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test4d</code>.</p>
 
@@ -586,7 +637,7 @@ modify your invalidation criteria in this way, but note that this is
 slightly different than the problem in 3c: Here you care about fields,
 where there you cared about method calls. </p>
 
-<h3>e. Invalidate, part 3</h3>
+<h3>4.e. Invalidate, part 3</h3>
 
 <p> <strong>Task:</strong> Pass <code>tests.Test4e</code>.</p>
 
@@ -598,5 +649,23 @@ go help other people.  Otherwise, you have fallen prey to our cruel
 trap: Remember that whenever a point moves it should invalidate the
 caches of <em>all</em> enclosing groups.  </p>
 
-<hr>
+<div class="instruction">
+
+<p> Congratulations!  Not only have you learned about how to
+program in AspectJ, you have worked through exercises paralleling
+a common AspectJ adoption strategy.  You should be able to
+pick up AspectJ and use it to improve your own software's
+crosscutting modularity. </p>
+
+<p> You can find the current binaries, source, documentation and
+an active user community for AspectJ at</p>
+
+  <blockquote>
+  http://www.eclipse.org/aspectj
+  </blockquote>
+  
+</div>
+
+<img style="newpage" src="figures_classes.gif" height="900" alt="" />
+
 </body> </html>