blob: fc47c757ad10d3246c6a36f4e89958b97e6818b2 [file] [log] [blame]
<?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". If you're in an A4
country, don't forget to modify the paper size.
The gif included at the end is somewhat fragile,
so be careful with different paper sizes.
TODO: There is currently something weird about PDF generation
from this: If generated from a windows machine, it will
generate mac-unfriendly PDF because of the requested windows
font. If the PDF is only used for immediate printing,
that's fine, but if it's used for distribution, bad.
-->
<head>
<title>Hands-on Programming with AspectJ&reg; &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>
<h1>Hands-on Programming with AspectJ<sup>&reg;</sup></h1>
<div class="presenter">Matt Chapman</div>
<div class="presenter">Matthew Webster</div>
<div class="presenter">Mik Kersten</div>
<div class="presenter">http://www.eclipse.org/aspectj</div>
<div class="presenter">http://www.eclipse.org/ajdt</div>
<h2>Overview</h2>
<p> In this tutorial you will solve some basic 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> 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. </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>
<!--
This space is used for a copyright that appears on the
bottom of the _printed_ page. It's suppressed when viewed on
a computer screen by the stylesheet.
<div id="copyright">
Copyright is held by the author/owner(s). <br />
OOPSLA’04, October 24-28, 2004, Vancouver, British Columbia, Canada <br />
2004 ACM 04/0010
</div>
-->
<h3 class="newpage">AspectJ Development in Eclipse</h3>
<p>The AspectJ Development Tools (AJDT) plugins for Eclipse
provide support for using the AspectJ compiler in the Eclipse environment.
Different versions of AJDT are available to support both the latest
release version of Eclipse, and the latest milestone build.
As part of the tutorial we will be providing distributions of base
eclipse, the AJDT plugins and the exercises. Should you wish to
install the exercises into an existing configuration on your machine,
you can download the exercises plugin via the following URL:
</p>
<a href="http://eclipse.org/ajdt/EclipseCon2006/org.aspectj.tutorial.exercises_1.0.0.jar">http://eclipse.org/ajdt/EclipseCon2006/org.aspectj.tutorial.exercises_1.0.0.jar</a>
<p>
Simply copy this JAR file to your eclipse/plugins folder.
</p>
<h3>Installation</h3>
<p>The key components to have installed are:</p>
<ul>
<li>An Eclipse distribution - 3.1.2 or 3.2M5
<li>An AJDT distribution - 1.3 or 1.4.dev
<li>An AspectJ Exercises plugin - 1.0.0
</ul>
<p>The best way to verify your installation is by attempting to
create one of the 4 tutorial projects. To do this, open File>New>Other...
as shown here (or press the New button on the toolbar):</p>
<p align="center"><img src="shots/FileNew.png"></p>
<p>You should then see the following option:</p>
<p align="center"><img src="shots/AJTutorials.png"></p>
<p>If you do, congratulations, you are ready to go !!</p>
<!-- ============================== -->
<h2 class="newpage">1. Static Invariants</h2>
<p> The easiest way to get started with AspectJ is to use it to
enforce static invariants, also known as compile time checks. These
can be as simple as verifying a particular API is never called or as
sophisticated as verifying your layered architecture is adhered to.
</p>
<p>The tutorial plugin includes four eclipse projects representing
the four major parts of this tutorial. Use the menus described in
the previous two screenshots to create each exercise - create
the first project now, <code>Exercise1</code>.</p>
<h3>1.a. Find old tracing</h3>
<div class="instruction"> <strong>Sample Exercise</strong>: The
main point of this exercise is to make sure your configuration
works.</div>
<p> <strong>Task:</strong> Signal an error for calls to
<code>System.out.println</code>.
</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 far longer than is necessary. Type in the aspect
below to signal an error at compile time if this mistake is made.
</p>
<p>This is your first aspect, in order to create the necessary Eclipse
resource into which you can type the answer, use the New Aspect Wizard.
You can access this wizard in two ways:</p>
<p>
1. From the New Java Class drop-down on the toolbar:
</p>
<p align="center"><img src="shots/NewAspectToolbarOption.png"></p>
<p>
2. From the File>New menu:</p>
</p>
<p align="center"><img src="shots/NewAspectMenuOption.png"></p>
<p>For this first aspect, fill it in as shown below:</p>
<p align="center"><img src="shots/NewAspectWizard.png"></p>
<p>After clicking <code>Finish</code> you should find yourself in the
new aspect, change it to look like this following code</p>
<p> <strong>Answer:</strong>
</p>
<pre>
package answers;
import figures.*;
aspect Answer {
declare error
: get(java.io.PrintStream System.out) &amp;&amp; within(figures..*)
: "illegal access to System.out";
}
</pre>
<div class="instruction"><strong>Note: </strong> For every task in this tutorial, you should work in the same
Answer aspect that you create at the start of the exercise, comment out
each previous answer as you move to the next task - if you don't then
you may unexpectedly get some test failures.</p></div>
<p> After saving, a build will take place and you'll find one incorrect
trace in <code>SlothfulPoint</code>:</p>
<p align="center"><img src="shots/Answer1a.png"></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 successfully finding the error, edit your
program to remove the illegal tracing call. </p>
<p> Make sure your program still passes the JUnit test
<code>tests.CoreTest</code> (which it should also pass at the beginning of
all exercises) before continuing. Every exercise in this tutorial includes
tests, each of which should be run individually. To run a particular test,
locate it in the <code>test-src</code> folder, then, right click the test
and navigate to the <code>Run>JUnit Test</code> menu option as shown here:</p>
<p align="center"><img src="shots/runningJUnitTests.png"></p>
<p>If it runs OK, you should get the trusty green bar:</p>
<p align="center"><img src="shots/junitTestsPassing.png"></p>
<p>When it passes, you are ready to move on!</p>
<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>,
the <code>void set*(..)</code> pattern
</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>
<pre>
aspect Answer {
declare warning: <em>&lt;pointcut here&gt;</em> : "bad field set";
}
</pre>
<p> where the pointcut picks out join points of private field sets
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.CoreTest</code> before continuing. Make sure you get eleven
warnings from this:</p>
<p align="center"><img src="shots/1b_11warnings.png"></p>
<p>Wait to fix them until the next exercise. </p>
<h3>1.c. Refine setters mandate</h3>
<p> <strong>Task:</strong> Allow assignmnents inside of constructors.
</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 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>
<p>You'll want to add another <code>withincode</code> primitive
pointcut to deal with the constructors.
</p>
<p>After you specify your pointcut correctly, you'll still find that
the convention is violated twice in the figures package. You should see
the following two errors:</p>
<pre>
.\figures\Point.java:37 bad field set
.\figures\Point.java:38 bad field set
</pre>
<p>Rewrite these two occurrences so as not to violate
the convention. Make sure your program still passes the JUnit test
<code>tests.CoreTest</code> before continuing. </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>
<!-- ============================== -->
<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, sometimes called runtime checks.
</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>2.a. Check a simple precondition</h3>
<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>
<p> <strong>Tools:</strong> <code>args</code>, <code>before</code>
</p>
<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 less than zero. </p>
<p> This should make the test case of <code>tests.Test2a</code> pass,
which wouldn't without your aspect. So before compiling in the
aspect:
</p>
<p align="center"><img src="shots/fail.png"></p>
<p> But after compiling in the aspect...
</p>
<p align="center"><img src="shots/pass.png"></p>
<p> <strong>Answer:</strong>
</p>
<pre>
package answers;
import figures.*;
aspect Answer {
before(int newValue): set(int Point.*) &amp;&amp; args(newValue) {
if (newValue &lt; 0) {
throw new IllegalArgumentException("too small");
}
}
}
</pre>
<h3>2.b. Check another precondition</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test2b</code>. </p>
<p> <strong>Tools: </strong> <code>call</code>
</p>
<p> <code>Group</code> is a <code>FigureElement</code> class that
encapsulates groups of other figure elements. As such, only actual
figure element objects should be added to <code>Group</code> objects.
Write an aspect to throw an <code>IllegalArgumentException</code>
whenever <code>Group.add()</code> is called with a <code>null</code>
value. </p>
<p> Look at <code>tests/Test2b.java</code> to see exactly what we're
testing for. </p>
<h3>2.c. Check yet another precondition</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test2c</code>. </p>
<p> <strong>Tools:</strong> <code>target</code>
</p>
<p> Another constraint on a well-formed group is that it should not
contain itself as a member (though it may contain other groups). Write
an aspect to throw an <code>IllegalArgumentException</code> whenever
an attempt is made to call <code>Group.add()</code> on a
<code>null</code> value, or on the group itself. </p>
<p> You will want to use a <code>target</code> pointcut to expose the
<code>Group</code> object that is the target of the <code>add</code>
call.
</p>
<h3>2.d. Assure input</h3>
<p> <strong>Task: </strong> Pass <code>tests.Test2d</code>.
</p>
<p> <strong>Tools: </strong> <code>around advice</code>
</p>
<p> Instead of throwing an exception when one of <code>Point</code>'s
<code>int</code> fields is set to a negative value, write an aspect
to trim the value to zero. You'll want to use <code>around</code>
advice that exposes the new value of the field assignment with an
<code>args</code> pointcut, and <code>proceed</code> with the trimmed
value. </p>
<p> This is going to look something like
</p>
<pre>
aspect Answer {
void around(int val): <var>&lt;Pointcut&gt;</var> {
<var>&lt;Do something with val&gt;</var>
proceed(val);
}
}
</pre>
<h3 class="newpage">2.e. Check a postcondition</h3>
<p> <strong>Task: </strong> Pass <code>tests.Test2e</code>
</p>
<p> <strong>Tools: </strong> <code>around advice</code>
</p>
<p> A postcondition of a <code>Point</code>'s <code>move</code>
operation is that the <code>Point</code>'s coordinates should change.
If a call to move didn't actually move a point by the desired
offset, then the point is in an illegal state and so an
<code>IllegalStateException</code> should be thrown.
</p>
<p> Note that because we're dealing with how the coordinates change
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>2.f. Check another postcondition</h3>
<p> <strong>Task: </strong> Pass <code>tests.Test2f</code>
</p>
<p> <strong>Tools:</strong> the <code> Rectangle(Rectangle)</code>
constructor, the <code>Rectangle.translate(int, int)</code> method.
</p>
<p> <code>FigureElement</code> objects have a <code>getBounds()</code>
method that returns a <code>java.awt.Rectangle</code> representing the
bounds of the object. An important postcondition of the general
<code>move</code> operation on a figure element 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 --
throw an <code>IllegalStateException</code> if it is violated. </p>
<!-- ============================== -->
<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>3.a. Simple tracing</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test3a</code>.</p>
<p> <strong>Tools:</strong>
<code>Log.write(String)</code>,
<code>thisJoinPoint.toString()</code>,
<code>execution</code>,
<code>within</code>
</p>
<p> Write an aspect to log the execution of all public methods
in the figures package. To do this, use the utility class
<code>Log</code> (this is in the <code>support</code> package, so
remember to import it into your answer aspect). Write a message
into the log with the static <code>write(String)</code> method.</p>
<h3>3.b. Exposing a value</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test3b</code>.</p>
<p> <strong>Tools:</strong> <code>target</code>
</p>
<p> AspectJ can expose the target object at a join point for tracing.
In this exercise, you will print not only the join point information,
but also the target object, with the form
</p>
<pre>
<em>thisJoinPointInfo</em> at <em>targetObject</em>
</pre>
<h3>3.c. More specialized logging</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test3c</code>.</p>
<p> <strong>Tools:</strong> <code>args</code>.
</p>
<p> Write an aspect to log whenever a <code>Point</code> is added to a
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 to see the trace message we expect you
to write in the log.
</p>
<h3 class="newpage">3.d. Logging extended to checking an invariant</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test3d</code>.</p>
<p> <strong>Tools:</strong> <code>inter-type field declaration</code>
</p>
<p> Make sure that a <code>Point</code> is never added to more
than one <code>Group</code>. To do so, associate a boolean flag
with each <code>Point</code> using an inter-type declaration,
such as </p>
<pre>
boolean Point.hasBeenAdded = false;
</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>3.e. Better error messages for 3.d.</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test3e</code>.</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>
<!-- ============================== -->
<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>
<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>4.a. Make a constant override</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test4a</code>.</p>
<p> <strong>Tools:</strong> <code>around</code>,
<code>FigureElement.MAX_BOUNDS</code>
</p>
<p> <code>Group</code>'s <code>getBounds()</code> method could be
understood to be a conservative approximation of the bounding box of a
group. If that is true, then it would be a legal (and much faster)
implementation of <code>getBounds()</code> to simply always return a
rectangle consisting of the entire canvas. The entire canvas is returned
by the static field <code>FigureElement.MAX_BOUNDS</code>.
</p>
<p> Write an aspect to implement this change. You can override
<code>Group</code>'s <code>getBounds()</code> method entirely with
around advice intercepting the method.
</p>
<h3>4.b. Make a constant cache</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test4b</code>.
</p>
<p> <strong>Tools:</strong> inter-type field.
</p>
<p> Instead of making the (very) conservative approximation of
<code>getBounds()</code> from part (a), write an aspect instead that
remembers the return value from the first time
<code>getBounds()</code> has been called on a <code>Group</code>, and
returns that first <code>Rectangle</code> for every subsequent
call. </p>
<p> <em>Hint: You can use an inter-type declaration to keep some
state for every <code>Group</code> object.</em> </p>
<h3>4.c. Invalidate, part 1</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test4c</code>.
</p>
<p> <strong>Tools:</strong> <code>before</code>
</p>
<p> While caching in this way does save computation, it will lead to
incorrect bounding boxes if a <code>Group</code> is ever moved.
Change your aspect so that it invalidates the cache whenever the
<code>move()</code> method of <code>Group</code> is called.
</p>
<h3 class="newpage">4.d. Invalidate, part 2</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test4d</code>.</p>
<p> Of course, part (c) didn't really solve the problem. What if a
<code>Point</code> that is part of a <code>Group</code> moves?
Whenever either of a Point's fields are set it should invalidate the
caches of all enclosing groups. Use your solution to problem 3c to
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>4.e. Invalidate, part 3</h3>
<p> <strong>Task:</strong> Pass <code>tests.Test4e</code>.</p>
<p> <strong>Tools:</strong> <em>You're on you're own</em></p>
<p> Did you really do part (d) correctly? Run the JUnit test
<code>tests.Test4e</code> to see. If you pass, congratulations, now
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>
<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>