Concurrent Programming in Java
© 1996-1999 Doug Lea  

 

 


1.4 Before/After Patterns

Follow-ups

Assertions

As of release 1.4, the assert keyword and AssertionError exceptions are built into Java, so you can more easily implement the illustrated checking methods. See below for an example. Of course, assertions could also be used instead of documentation comments or checking methods in many other sections of the book.

Dynamic Proxies

As of release 1.3, you can automatically generate java.lang.reflect.Proxy objects that selectively forward or intercept invocations. This presents an occasionally useful alternative for implementing before/after wrappers. The main advantages of this approach are: The main disadvantage is performance. Invocation via reflection takes more time than does the simple forwarding used in Adaptors. While the performance of dynamic proxies has improved in recent releases, the intrinsic need to perform comparisons filtering out the methods of interest means that they are unlikely to be as efficient as other solutions.

Here is an example of using dynamic proxies for the Tank class used for illustrating other techniques in the book:

import java.reflect.*;

class TankInterceptor implements java.lang.reflect.InvocationHandler {
  private final Tank delegate;

  // Store Method object to be intercepted to simplify and speed up checks
  private final Method transferMethod;

  public TankInterceptor(Tank tank) {
    delegate = tank;

    Method tm = null;
    try {
      tm = Tank.class.getMethod("transferWater", 
                                new Class[] { Float.TYPE });
    }
    catch (NoSuchMethodException cannotHappen) {
      assert false;
    }
    transferMethod = tm;
  }
  
  public Object invoke(Object proxy, Method method, Object[] args) 
    throws Throwable {

    // Intercept only the transferWater method
    if (transferMethod.equals(method)) {
      checkVolumeInvariant();
      try {
        return method.invoke(delegate, args);
      }
      finally {
        checkVolumeInvariant();
      }
    }

    // Pass through all other invocations
    else 
      return method.invoke(delegate, args);
   }


  protected void checkVolumeInvariant() {
    float v = delegate.getVolume();
    float c = delegate.getCapacity();
    assert (v >= 0.0 && v <= c);
  }

}

class UsageExample {

  static Tank newProxyTank() { // factory method
    return (Tank) 
      Proxy.newProxyInstance(Tank.class.getClassLoader(), 
                             new Class[] { Tank.class }, 
                             new TankInterceptor(new TankImpl()));
  }

  void example() throws OverflowException, UnderflowException {
    Tank tank  = newProxyTank();
    tank.transferWater(100);
  }
}

Thanks to Shiraz Wasim Zaidi for inspiring this example.

Readings and Resources

There are many useful design patterns besides those that are particular to concurrent programming, and surely many others relating to concurrency that are not included in this book. Other books presenting patterns and pattern-related aspects of software design include: The OO language Self is among the few that directly support a pure delegation-based style of programming without requiring explicit message forwarding. See: Reflective before/after techniques are often seen in Lisp, Scheme and CLOS (the Common Lisp Object System). See, for example: Additional layered synchronization design patterns are discussed in: A compositional approach to layering concurrency control is described in: Composition of collections of before/after methods that deal with different aspects of functionality (for example, mixing synchronization control with persistence control) may require more elaborate frameworks than discussed here. One approach is to construct a metaclass framework that partially automates the interception and wrapping of methods by class objects. For an extensive analysis and discussion of the resulting composition techniques, see: Aspect-oriented programming replaces layered before/after techniques with tools that weave together code dealing with different aspects of control. Reports on the language AspectJ include some examples from this book expressed in an aspect-oriented fashion. See: Several tools are available for partially automating invariant tests. See, for example:


Doug Lea
Last modified: Tue Oct 26 11:45:14 EDT 1999