Object Oriented Design Course : Exercise 5
Exercise 5 - Design by contract in Java
Deadline June 23rd, 2004 at midnight
Description The goal of this exercise is to experience both Aspect-Oriented Programming and Dynamic Proxies in Java, and compare them both. The way to do this will be to write a tiny framework that support Design by Contract. Users can write an "assertion class" for each class, that will contain code for preconditions, postconditions and an invariant. The checked class or the code that uses it will be unaware of the assertion class, and of the mechanism that runs the assertions.

Writing Assertions in the framework

Assume we have the following interface and class:
interface PositiveDouble {
  public void setValue(double val);
  public double getValue();
  public double getSqrt();
  public double getSum(double e);
  public void add(double e);
}

class PositiveDoubleImpl
  implements PositiveDouble {

  private double d;

  public void setValue(double val) {
    d = val;
  }
  public double getValue() {
    return d;
  }
  public double getSqrt() {
    return Math.sqrt(d);
  }
  public double getSum(double e) {
    return d + e;
  }
  public void add(double e) {
    d += e;
  }
}
We would like to add assertions for it. Specifically, we'd like to check the invariant that the number is positive, add postcondtion to add() checking the result is positive and add a postcondition to getSqrt(), that says that multiplying its returned value by itself gives d.
To do so, we'll write a separate class that will hold the assertions for PositiveDouble. This class will implement the interface Assertion, defined as follows:
interface Assertion {

  /**
   * Pass the checked object to the assertion class.
   * Ensured to be called before any other method of the assertion class.
   */
  public void setObject(Object o);

  /**
   * The class invariant
   */
  public boolean invariant();

}
This will be the code for the assertion class we want:
class PositiveDoubleAssertion
  implements Assertion {

    private PositiveDouble pd;

    public void setObject(Object o) {
      pd = (PositiveDouble) o;
    }

    public boolean invariant() {
      return pd.getValue() >= 0;
    }

    public boolean pre_setValue(double d) {
      return d >= 0;
    }

    public boolean post_getSqrt(double result) {
      // Question: why do we use this condition and not simple eauility (==) ?
      return Math.abs(result * result - pd.getValue()) <= 0.001;
    }

    public boolean post_getSum(double e, double result) {
      return result == pd.getValue() + e;
    }

    public boolean pre_add(double e) {
      return pd.getValue() + e >= 0;
    }
  }
In order to use assertions, the assertion class should have the following properties:
  • Implement the ood.dbc.Assertion class
  • The class name should be <CheckedClassName>Assertion. The package name does not matter. For example, the assertion class of the class Rectangle will be RectangleAssertion.
  • For each method of the tested class, the assertion can add a pre-condition and post-condition test. This methodsd will have the following signiture:
    public boolean pre_<checked-method-name>(<checked-method-argument-list>)
    public boolean post_<checked-method-name>(<checked-method-argument-list>, <checked-method-return-value>)
    Theses methods (invariant(), pre_*(), post_*()) return true if everything is allright, and false if one of their tests fail. Notice the slight difference between the argument list in both methods - in the pre-condition it is the same as the checked method, and in the post-condition it is the same as the checked method plus the returned value as the last argument.
Examples can be shown in the above PositiveDoubleAssertion assertion class.

The framework does not take care of the following areas:
  • Class constructors and finalizers
  • Class inheiritance
  • Static methods
  • Concrete classes (the framework can handle interfaces only)
  • Give access to old status in the post-conditions

Running the framework - Dynamic Proxies

In the dynamic proxies approach, you have to register each object that you'd like to check separately. To do this, you need to write a class called DBCProxy, that will have these public static methods:
class DBCProxy {

  /**
   * Tells the DBCProxy in which package to search for <code>Assertion</code> implementations.
   */
  public static void setAssertionsPackage(String packageName);

  /**
   * Wraps the given object implementation with a dynamic proxy.
   */
  public static Object getCheckedObject(Object objectImpl);
}

To create a new PositiveDouble object, the following code needs to be called:
DBCProxy.setAssertionsPackage("my.math.assertions");
PositiveDouble pd = (PositiveDouble) DBCProxy.getCheckedObject(new PositiveDoubleImpl());

Make sure you call setAssertionsPackage() before getCheckedObject(). The reason for that is that getCheckedObject() searches the assertion class (according to the naming convention) in the given directory.

The framework works as follows: All defined assertions will run for the object 'pd' initialized above. The framework engine needs to catch all method calls to 'pd', check whether pre- or post-conditions are defined for each method (according to the naming convention for such methods), and run them.
If an assertion fails (the assertion method returns false), then a RuntimeException with a detailed error message should be raised. The message should include the class name, method name, the type of assertion (pre/post/invariant) that was violated, and the actual parameters of the method call that failed.

Tip: This is very similar to the dynamic proxy example from class.

Running the framework - AspectWerkz

The second part of the exercise is implementing the class AssertingAspect which will inject the user's assertions to the desired code. Assume that the user will supply the aspectwerkz.xml defining which classes will be checked.

The AssertingAspect class has the following interface:

public class AssertionAspect {

	/**
	 * A hook for adding assertion to this join-point
	 * @param joinPoint the method where we add the assertion
	 * @return the result of the activating the join-point
	 * @throws Throwable in case the checked method threw
	 */
	public Object checkAssertionAdvice(JoinPoint joinPoint) throws Throwable;

}
As you can see this is an aspect class with three advices for pre-conditions, post-conditions and invariants checking. The assertion package will be set by the user in the assertion.package system property (See here to learn about system properties).

Example aspectwerkz.xml:

<?xml version="1.0"?>

<!DOCTYPE aspectwerkz PUBLIC
    "-//AspectWerkz//DTD 0.10//EN"
    "http://aspectwerkz.codehaus.org/dtd/aspectwerkz_0_10.dtd">

<aspectwerkz>
  <system id="dbc">
    <aspect class="ood.dbc.AssertionAspect">

       <pointcut name="check_assertion"
                 expression="..."/>

       <advice name="checkAssertionAdvice"
               type="around"
               bind-to="check_assertion"/>

    </aspect>
  </system>
</aspectwerkz>

To run the "aspected" framework, the user will use the aspectwerkz executable (instead of java)

Design Questions

In your README, address the following issues:
  1. Make a comparison between the two methods of injecting the assertions. Specifically, address the issues of flexibility, performance and ease of use (both to the framework developer and to the framework user).
  2. How would you extend the framework so it would support inheritance?
  3. How would you extend the framework so it would support static methods?

Compiling & Running

You need to supply a build.xml file, executed by ant (instead of Makefile). Calling ant within the project's dircetory will compile your code and run the unit tests.

No main class is needed. The user is supposed to supply the main class of the application and use its classes

In order to make Ant and AspectWerkz work in your system, add the following lines to you .cshrc or .classpath:

#Ant
setenv ANT_HOME /usr/share/ant
setenv CLASSPATH "$CLASSPATH":"$ANT_HOME/lib"
setenv PATH "$PATH":"$ANT_HOME/bin"

#AspectWerkz
setenv ASPECTWERKZ_HOME /cs/course/current/ood/aspectwerkz
setenv CLASSPATH "$CLASSPATH":"$ASPECTWERKZ_HOME/lib"
setenv PATH "$PATH":"$ASPECTWERKZ_HOME/bin"
If you are writing this exercise outside the department's environment, download Ant from here and AspectWerkz from here. Don't forget to add the above environment variables to your system.

A Final Note

The source of all the examples can be viewd here, and an example project (which you can base your project on) can be downloaded from here.

In this exercise you develop a framework, not a working tool or application. You can (and welcomed!) to use our examples as tests for your implementation. You are also welcomed to publish test files (classes and assertions) in the newsgroup for the use of other students.

As usual, don't forget to add the unit tests (and make sure they provide good covrage) and the UML diagrams of your code. As in exersice 3, export them to image files and display them in the README.html.

Submission Submit a zip file contains all your sources, a README.html, the build.xml and the shell script.
Resources