From ce801a083791f978fb69fc7cb362109b688a98c1 Mon Sep 17 00:00:00 2001 From: ehilsdal Date: Thu, 28 Aug 2003 23:46:06 +0000 Subject: [PATCH] mostly final edits --- docs/teaching/exercises/answers/Answer3d.java | 30 +++ docs/teaching/exercises/answers/Answer3e.java | 30 +++ docs/teaching/exercises/base.lst | 2 + docs/teaching/exercises/index.html | 251 ++++++++---------- docs/teaching/exercises/tests/Test3d.java | 38 +++ docs/teaching/exercises/tests/Test3e.java | 39 +++ 6 files changed, 248 insertions(+), 142 deletions(-) create mode 100755 docs/teaching/exercises/answers/Answer3d.java create mode 100755 docs/teaching/exercises/answers/Answer3e.java create mode 100755 docs/teaching/exercises/tests/Test3d.java create mode 100755 docs/teaching/exercises/tests/Test3e.java diff --git a/docs/teaching/exercises/answers/Answer3d.java b/docs/teaching/exercises/answers/Answer3d.java new file mode 100755 index 000000000..6a3238b5f --- /dev/null +++ b/docs/teaching/exercises/answers/Answer3d.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package answers; + +import support.Log; + +import figures.*; + +aspect Answer3d { + + boolean Point.inGroup = false; + + before(Point p): execution(void Group.add(FigureElement)) && args(p) { + if (p.inGroup) { + throw new IllegalStateException(); + } else { + p.inGroup = true; + } + } +} diff --git a/docs/teaching/exercises/answers/Answer3e.java b/docs/teaching/exercises/answers/Answer3e.java new file mode 100755 index 000000000..8e0018df8 --- /dev/null +++ b/docs/teaching/exercises/answers/Answer3e.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package answers; + +import support.Log; + +import figures.*; + +aspect Answer3e { + + Group Point.containingGroup = null; + + before(Group g, Point p): execution(void Group.add(FigureElement)) && this(g) && args(p) { + if (p.containingGroup != null) { + throw new IllegalStateException(p.containingGroup.toString()); + } else { + p.containingGroup = g; + } + } +} diff --git a/docs/teaching/exercises/base.lst b/docs/teaching/exercises/base.lst index 9ac735b58..cb6914637 100644 --- a/docs/teaching/exercises/base.lst +++ b/docs/teaching/exercises/base.lst @@ -20,6 +20,8 @@ tests/Test2f.java tests/Test3a.java tests/Test3b.java tests/Test3c.java +tests/Test3d.java +tests/Test3e.java tests/Test4a.java tests/Test4b.java tests/Test4c.java diff --git a/docs/teaching/exercises/index.html b/docs/teaching/exercises/index.html index 755c15f9d..3d459358d 100644 --- a/docs/teaching/exercises/index.html +++ b/docs/teaching/exercises/index.html @@ -1,26 +1,39 @@ -AspectJ Tutorial Exercises +AspectJ Exercises -

AspectJ Tutorial Exercises

+

AspectJ Exercises

Organization

-

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.

- -

Before the tutorial we will make available a 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 setpaths.bat or source setpaths to -export some other needed environment variables.

+

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.

+ +

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.

+ +

Command-line usage

+ +

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 setpaths.bat or +source setpaths to export some other needed environment +variables.

All the files in the program are listed in base.lst, including test cases and an empty answer aspect, @@ -29,7 +42,7 @@ answers there, all you need to do is compile base.lst, either in an IDE or with

-$ ajc -Xlint -argfile base.lst
+$ ajc -argfile base.lst
 

Before you move onto another exercise, though, make sure to copy @@ -39,18 +52,11 @@ together:

 > copy answers/Answer.java answers/2a.java  (Windows)
-$ cp answers/Answer.java answers/2a.java    (Linux)
+$ cp answers/Answer.java answers/2a.java    (Unix)
 
-

If you want to put your answer in a different file, say, -answers/Answer2a.java, you can compile with

- -
-$ ajc -Xlint -argfile base.lst answers/Answer2a.java
-
- -

In any case, after building the system, you should invoke Java on -the compiled test class. On the command-line, this this would be

+

After building the system, you should invoke Java on the compiled +test class. On the command-line, this this would be

 $ java tests.Test2a
@@ -66,7 +72,7 @@ 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. 

-

Again, ae will be looking at some solutions and having discussion, +

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 @@ -91,13 +97,15 @@ shell, though, to run the JUnit tests (and don't forget to run the set up the run button to run a test through the Options menu, but we've found this is fairly cumbersome.

- -

1. Static Invariants

+

The easiest way to get started with AspectJ is to use it to +enforce static invariants. +

+

a. Catch old tracing

Sample Exercise: The main point of this exercise @@ -114,7 +122,7 @@ test.

to use System.out.println(), 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 forces an error at compile time if this mistake is made. +below to force an error at compile time if this mistake is made.

When you use this on the given system, you'll find one incorrect @@ -163,6 +171,7 @@ aspect Answer1a { of the out field is illegal. This will also catch those users who bind System.out to a static field to save typing.

+

b. Mandate setters

@@ -191,9 +200,8 @@ outside of setter methods. "Outside", here, means that the code for the assignment is outside the text of the setter.

Make sure your program still passes the JUnit test -tests.Test before continuing, and that you see all of the -following warning messages. Make sure you get 11 warnings from this. -Wait to fix them until the next exercise.

+tests.Test before continuing. Make sure you get 11 +warnings from this. Wait to fix them until the next exercise.

c. Refine setters mandate

@@ -205,9 +213,9 @@ Wait to fix them until the next exercise.

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 -setter methods or constructors. Modify your answer (in a new -file) to signal an actual error at compile time (rather than just a -warning) when such an illegal assignment expression exists.

+setter methods or constructors. Modify your answer to signal +an actual error at compile time (rather than just a warning) when such +an illegal assignment expression exists.

You'll want to add another withincode primitive pointcut to deal with the constructors. @@ -217,33 +225,38 @@ pointcut to deal with the constructors. the convention is violated twice in the figures package. You should see the following two errors:

-
-.\figures\Point.java:28:9: bad field set
-        _x += dx;
-        ^
-.\figures\Point.java:29:9: bad field set
-        _y += dy;
-        ^
+
+.\figures\Point.java:37 bad field set
+.\figures\Point.java:38 bad field set
+
 2 errors
-
+

Rewrite these two occurrences so as not to violate the convention. Make sure your program still passes the JUnit test tests.Test before continuing.

-

d. Congratulatoins

+

d. 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.

-
-

2. Dynamic invariants

+

The next step in AspectJ adoption is often to augment a test suite +by including additional dynamic tests. +

+ +

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.

+

a. Check a simple precondition

Sample Exercise: We've provided the answer to @@ -280,7 +293,7 @@ Tests run: 7, Failures: 2, Errors: 0

-$ ajc -Xlint -argfile base.lst answers/Answer.java
+$ ajc -argfile base.lst answers/Answer.java
 
 $ java tests.Test2a
 .......
@@ -310,6 +323,9 @@ aspect Answer2a {
 
 

Task: Pass tests.Test2b.

+

Tools: call. +

+

Group is a FigureElement class that encapsulates groups of other figure elements. As such, only actual figure element objects should be added to Group objects. @@ -340,19 +356,32 @@ call.

d. Assure input

-

Task: Pass tests.Test2g. +

Task: Pass tests.Test2d.

Tools: around advice

Instead of throwing an exception when one of Point's -int fields are set to an out-of-bounds value, write an -aspect to trim the value into an in-bounds one. You'll want to use -around advice that exposes the new value of the field -assignment with an args pointcut, and -proceed with the trimmed value.

+int fields are set to a negative value, write an aspect +to trim the value to zero. You'll want to use around +advice that exposes the new value of the field assignment with an +args pointcut, and proceed with the trimmed +value.

+ +

This is going to look something like +

+ +
+aspect A {
+    void around(int val): <Pointcut> {
+        <Do something with val>
+        proceed(val);
+    }
+}
+
+

e. Check a postcondition

Task: Pass tests.Test2e @@ -363,7 +392,7 @@ assignment with an args pointcut, and

A postcondition of a Point's move operation is that the Point's coordinates should change. -If a call to move move didn't actually move a point by the desired +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 IllegalStateException should be thrown.

@@ -389,22 +418,14 @@ element's bounds rectangle should move by the same amount as the figure itself. Write an aspect to check for this postcondition -- throw an IllegalStateException if it is violated.

-

Help Yourself by Helping Others

- -

At this point, check the people to your left and right. If -they're stuck somewhere, see if you can help them.

- -
-

3. Tracing

-

The crosscutting feature you will be adding in part (4) will be -support for caching the bound objects of Group figure -elements, which may be costly to compute. On the way to that, though, -it's useful to explore the system with some tracing aspects.

+

Tracing is one of the classic AspectJ applications, and is often +the first where AspectJ is used on deployed code. +

a. Simple logging

@@ -420,7 +441,7 @@ in the figures package. To do this, use the utility class Log (with an import from support.Log) and call Log.log(String)

-

b. Simple logging

+

b. Exposing a value

Task: Pass tests.Test3b.

@@ -441,35 +462,16 @@ but also the target object, with the form

Task: Pass tests.Test3c.

-

Tools: +

Tools: args.

-

Write an aspect to log whenever a Point is added to -a group (including initially). To do this, use the utility class -Log (with an import from support.Log) and -call

- -
-Log.log("adding Point")
-
- -

This will write the string "adding Point", followed by a semicolon -terminator, to the Log. For example, with your aspect enabled,

- -
-Point p1 = new Point(10, 100);
-Point p2 = new Point(10, 100);
-Group g = new Group(p1);
-g.add(p2);
-System.out.println(Log.getString());
-
+

Write an aspect to log whenever a Point is added to a +group. The args pointcut allows you to select join points +based on the type of a parameter to a method call.

-

should print out "adding Point;adding Point;". +

Look at the test case for details about the tested log message.

-

Hint: The args pointcut allows you to select join points -based on the type of a parameter to a method call.

-

d. Logging extended to checking an invariant

Task: Pass tests.Test3d.

@@ -477,8 +479,18 @@ based on the type of a parameter to a method call.

Tools: inter-type field declaration

-

Make sure that a Point is never added to more than one Group.

+

Make sure that a Point is never added to more than one Group. +To do so, associate a boolean flag with each Point using an inter-type +declaration, such as

+ +
+boolean Point.hasBeenAdded = false;
+
+

Check and set this flag with the same kind of advice from your +answer to problem (c). Throw an IllegalStateException if +the point has already been added. +

e. Better error messages for 3d

@@ -487,53 +499,9 @@ based on the type of a parameter to a method call.

Tools:

-

Make sure that a Point is never added to more than one Group. -Include the Group that the Point was previously a part of in the -thrown exception.

- -

c. Keeping track of state

- -

In this exercise, perform the tracing from part (a), but also log -the enclosing group, if any, of the moving point. You can use an -inter-type declaration inside your aspect to associate a -Group field with Point objects, and then -work with that field, setting it appropriately when the -Point is added to a Group (at the same join -points you were tracing in part b). So

- -
-Point p1 = new Point(10, 100);
-p1.move(0, 0);
-System.out.println(Log.getString());
-
- -

should print out "moving as a part of null;", but -

- -
-Point p1 = new Point(10, 100);
-Group g = new Group(p1);
-p1.move(0, 0);
-System.out.println(Log.getString());
-
- -

should print out "moving as a part of Group(Point(10, 100));", -which you can do by using the toString() method already defined on -Group.

- -

Hint: This exercise combines the tracing from parts a and b. -If you start with an aspect that includes the solutions to those -previous exercises, you'll be most of the way there.

- -

Test this with the JUnit test case tests.Test3c. - - -

Help Yourself by Helping Others

- -

At this point, check the people to your left and right. If -they're stuck somewhere, see if you can help them.

- -
+

Extend your solution to problem (d) by using the string +representation of the Point's containing group as the msg +part of the IllegalStateException.

@@ -577,7 +545,7 @@ around advice intercepting the method.

Task: Pass tests.Test4b.

-

Tools: private Rectangle Group.mumble; +

Tools: inter-type field.

Instead of making the (very) conservative approximation of @@ -605,12 +573,11 @@ Change your aspect so that it invalidates the cache whenever the move() method of Group is called.

+

d. Invalidate, part 2

Task: Pass tests.Test4d.

-

Tools: your solution to 3c

-

Of course, part (c) didn't really solve the problem. What if a Point that is part of a Group moves? Whenever either of a Point's fields are set it should invalidate the diff --git a/docs/teaching/exercises/tests/Test3d.java b/docs/teaching/exercises/tests/Test3d.java new file mode 100755 index 000000000..4fe8a04ca --- /dev/null +++ b/docs/teaching/exercises/tests/Test3d.java @@ -0,0 +1,38 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package tests; + +import figures.*; +import support.Log; +import junit.framework.*; +import java.util.List; +import java.util.Arrays; + +public class Test3d extends Test { + + public static void main(String[] args) { + junit.textui.TestRunner.run(Test3d.class); + } + + public void testDuplicateAdd() { + Log.clear(); + Point p1 = new Point(10, 100); + Group g1 = new Group(p1); + try { + Group g2 = new Group(p1); + fail("should have thrown IllegalStateException"); + } catch (IllegalStateException ea) { + } + } +} + diff --git a/docs/teaching/exercises/tests/Test3e.java b/docs/teaching/exercises/tests/Test3e.java new file mode 100755 index 000000000..d9f6b9c35 --- /dev/null +++ b/docs/teaching/exercises/tests/Test3e.java @@ -0,0 +1,39 @@ +/* ******************************************************************* + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * PARC initial implementation + * ******************************************************************/ + +package tests; + +import figures.*; +import support.Log; +import junit.framework.*; +import java.util.List; +import java.util.Arrays; + +public class Test3e extends Test { + + public static void main(String[] args) { + junit.textui.TestRunner.run(Test3e.class); + } + + public void testDuplicateAdd() { + Log.clear(); + Point p1 = new Point(10, 100); + Group g1 = new Group(p1); + try { + Group g2 = new Group(p1); + fail("should have thrown IllegalStateException"); + } catch (IllegalStateException ea) { + assertEquals(g1.toString(), ea.getMessage()); + } + } +} + -- 2.39.5