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:
- All before/after mechanics can be defined in one place
(InvocationHandler.invoke) rather than duplicated
for each method.
- Methods that do not need interception can just be
"passed through" rather than manually forwarded. This
approach can be more robust in the face of future changes in
the ground classes.
- The resulting Proxy classes can be generated at run-time.
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:
-
Buschmann, Frank, Regine Meunier, Hans Rohnert, Peter Sommerlad,
and Michael Stal. Pattern-Oriented Software Architecture: A System
of Patterns, Wiley, 1996.
- Coplien, James. Advanced C++: Programming Styles and Idioms,
Addison-Wesley, 1992.
- Fowler, Martin. Analysis Patterns, Addison-Wesley, 1997
- Gamma, Erich, Richard Helm, Ralph Johnson, and John
Vlissides. Design Patterns, Addison-Wesley, 1994. (The "Gang of
Four" book.)
-
Rising, Linda. The Patterns Handbook, Cambridge University Press,
1998.
-
Shaw, Mary, and David Garlan. Software Architecture, Prentice
Hall, 1996.
-
(Various editors) Pattern Languages of Program Design,
Addison-Wesley. This series incorporates patterns presented at the
annual Pattern Languages of Programming (PLoP) conference.
- The Patterns home page.
The OO language Self is among the few that directly support a pure
delegation-based style of programming without requiring explicit
message forwarding. See:
-
Ungar, David. "The Self Papers", Lisp and Symbolic Computation,
1991.
Reflective before/after techniques are often seen in Lisp, Scheme
and CLOS (the Common Lisp Object System). See, for example:
-
Abelson, Harold, and Gerald Sussman. Structure and Interpretation
of Computer Programs, MIT Press, 1996.
- Kiczales, Gregor, Jim des Rivieres, and Daniel Bobrow. The Art
of the Metaobject Protocol, MIT Press, 1993.
Additional layered synchronization design patterns are discussed in:
-
Rito Silva, António, João Pereira and José Alves Marques. "Object
Synchronizer", in Neil Harrison, Brian Foote and Hans Rohnert
(eds.), Pattern Languages of Program Design, Volume 4,
Addison-Wesley, 1999. See also the DASCo web site.
A compositional approach to layering concurrency control is described in:
-
Holmes, David. Synchronisation Rings: Composable Synchronisation
for Concurrent Object Oriented Systems, PhD Thesis, Macquarie
University, 1999.
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:
-
Forman, Ira, and Scott Danforth. Putting Metaclasses to Work,
Addison-Wesley, 1999.
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:
-
Kiczales, Gregor, John Lamping, Anurag Mendhekar, Chris Maeda,
Cristina Videira Lopes, Jean-Marc Loingtier, and John
Irwin. "Aspect-Oriented Programming", Proceedings of the European
Conference on Object-Oriented Programming (ECOOP), 1997.
Several tools are available for partially automating invariant
tests. See, for example:
-
Beck, Kent, and Erich Gamma. "Test Infected: Programmers Love
Writing Tests", The Java Report, July 1998.
- iContract.
Doug Lea
Last modified: Tue Oct 26 11:45:14 EDT 1999