Concurrent Design Pattern Framework Implementation and Examples

This file is part of the Panini project at Iowa State University. The contents of this file and other files and folder contained in this distribution are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use these file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/.

Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License.

For more details and the latest version of this code please see http://paninij.org/patterns.

The Initial Developer of the Original Code is Iowa State University. Portions created by Iowa State University are Copyright (C) 2010, Iowa State University of Science and Technology. All Rights Reserved.

Contributors: Hridesh Rajan, Steven M. Kautz, Wayne Rowcliffe, Sean L. Mooney, and Brittin Fontenot.

Contents of the archive

lib jar file containing the framework classes
lib-external additional jar files needed by some of the examples
license license for this project
src source code
The src folder contains the source code for the concrete examples as well as the library classes used to convert the sequential patterns into concurrency-enhanced patterns.

Dependencies

The library classes themselves are precompiled as a jar file in the lib directory named asynclib.x.y.z.jar (where x.y.z is the version number). All examples depend upon this jar file. In addition, some examples depend on external libraries in the external-lib directory (indicated in the README for the particular example).

Annotation processing

Many of the patterns depend on autogenerated code. By default, code is autogenerated at runtime and then compiled using the dynamic compiler API and loaded using a custom classloader that is part of the library. However, it is generally preferable to have the necessary code generated at compile time using the JDK annotation processing tool. Annotations are used, as described in the table below, to identify the participants in a particular pattern. (Note: JDK 1.6 is required.)

For further details on how the patterns library uses autogenerated code, see the technical report at http://paninij.org/patterns.

Examples

The bulk of this distribution consists of examples demonstrating the Gang of Four (GOF) design patterns implemented in both a sequential style and using a concurrency-enhanced form of the pattern. The root package for the examples is org/paninij/concurrentPatterns/examples. Each example has its own package with both a sequential and concurrent version. In most cases there is a README in the sequential version that describes the example and a README in the concurrent version describing the adaptation to the concurrency-enhanced pattern.

Building the examples with the Ant build script

All examples can normally be built using the build target. By default, the annotation processing tool places autogenerated code in the bin directory. Class files and autogenerated code can be removed using the clean target.

Building the examples with Eclipse

  1. Create a new, empty Java project
  2. Drag the src, lib, and lib-external directories into the project directory in the Package Explorer. Allow the existing src directory to be overwritten.
  3. In the project Properties add the five jar files in lib and lib-external to the build path.
At this point, the examples should all compile. However, since annotation processing is not yet enabled, most examples will require the autogenerated code to be created, compiled, and loaded at runtime, often creating a noticeable pause in execution.

To enable annotation processing in Eclipse:

  1. In Project Properties -> Java Compiler, be sure compiler compliance level to 1.6
  2. In Project properties -> Java Compiler -> Annotation Processing, check "Enable project specific settings" and "Enable annotation processing" ("Enable processing in editor" doesn't matter, it won't work anyway). The generated source directory: default is .apt_generated, which is fine.
  3. In Project properties -> Java Compiler -> Annotation Processing -> Factory Path, check "Enable project specific settings", click click "Add JARs" and browse to lib/asynclib-x.y.z.jar
  4. Confirm in the next dialog that the project will be rebuilt.
After a rebuild, there should be a directory .apt_generated beneath the project directory that contains the source files for the generated classes. Building all the examples should result in four packages under .apt_generated:

Running the examples

Most examples have an obvious entry point such as "Main". The general package structure and dependencies are described above. For example, from the top-level directory a typical command line might look like:
java  -cp lib/asynclib-0.1.3.jar;bin  org.paninij.concurrentPatterns.examples.abstractFactory.concurrent.financial.Main

Using the library

Compiling with javac

To use the library, include lib/asynclib-x.y.z.jar on the build classpath. The JDK 1.6 compiler will automatically discover the annotation processor by examining the META-INF/services directory in the jar file. Alternatively it is possible to use the compiler option
 -processor  org.paninij.concurrentPatterns.lib.aproc.AsyncProcessor
However, in either case there is a catch, which is that all source files containing annotations must be explicitly given as arguments to the javac command (either by name or via a wildcard). For example, compiling Main.java in any of the example packages will always cause the remaining classes in the package to be implicitly compiled, but annotations in those files will not be processed, resulting in the warning
warning: Implicitly compiled files were not subject to annotation processing.
Use -proc:none to disable annotation processing or -implicit to specify a policy
 for implicit compilation.
(Note that the use of the -implicit:class option to javac will suppress the warning, but will NOT change the behavior of the annotation processor.) For a single package it is normally sufficient to compile *.java instead of Main.java. To compile an entire project it is probably simplest to generate a file containing a complete list of java source files and pass it to the javac command using the @ option, e.g.,
find src | grep .java > argfile
javac -cp your-class-path  @argfile

Compiling with Eclipse

To use the library in a project, include lib/asynclib-x.y.z.jar on the build classpath for the project. Follow the
instructions above to enable annotation processing in Eclipse.

Usage summary

(See the example code for further details.)
Pattern Generated code Annotations Usage hint
Abstract factory Yes @AbstractFactory (1) Given a factory interface IFactory and a concrete implementation ConcreteFactory, replace the call

IFactory f = new ConcreteFactory();

with

IFactory f = AsyncUtil.createAsyncFactory(IFactory.class, new ConcreteFactory());

If the IFactory interface is annotated @AbstractFactory, the autogenerated classes can be created at compile time.

Builder Yes @Builder (1) Given an interface IBuilder with various methods representing steps in creation of a product, along with a concrete implementation ConcreteBuilder, replace the call

IBuilder b = new ConcreteBuilder();

with

IBuilder b = AsyncUtil.createAsyncBuilder(IBuilder.class, new ConcreteBuilder());

If the IBuilder interface is annotated @Builder, the autogenerated classes can be created at compile time.

Factory method Yes @FactoryMethod Annotate factory methods in a class C with @FactoryMethod. Call AsyncUtil.createAsyncFactoryMethods(C instance) to obtain an instance of a subtype of C in which the annotated methods are executed asynchronously. Annotated methods must return an interface.
Prototype No None Given an instance obj of a Cloneable class C, replace code such as

C newInstance = obj.clone();
// do other stuff...
newInstance.foo();

with

Future handle = AsyncPrototype.createClone(obj);
// do other stuff...
C newInstance = AsyncPrototype.getClone(handle);
newInstance.foo();

Singleton X X X
Adapter Yes @Adapter (1) Given an interface ITarget and a concrete implementation Adapter, replace the call

ITarget adapter = new Adapter(new Adaptee());

with

ITarget adapter = AsyncUtil.createAsyncAdapter(ITarget.class, new Adapter(new Adaptee());

If the Adapter class is annotated @Adapter, the autogenerated classes can be created at compile time.

Bridge X X X
Composite No None Components extend abstract class ConcurrentComposite or ConcurrentLeaf.
Decorator Yes @Decorator, @AddedBehavior Annotate abstract decorator class with @Decorator. In concrete decorator of class C, annotate methods that add behavior using @AddedBehavior. Call AsyncUtil.createAsyncDecorator(C c) to obtain a subtype of C in which @AddedBehavior methods are executed asynchronously. See example for additional usage restrictions.
Facade Yes @Facade (1) Given an interface IFacade and a concrete implementation Facade, replace the call

IFacade f = new Facade();

with

IFacade f = AsyncUtil.createAsyncFacade(IFacade.class, new Facade();

If the IFacade interface is annotated @Facade, the autogenerated classes can be created at compile time.

Flyweight X X X
Proxy Yes @Proxy (1) Given an interface IProxy and a concrete implementation ConcreteProxy, replace the call

IProxy p = new ConcreteProxy();

with

IProxy p = AsyncUtil.createAsyncProxy(IProxy.class, new ConcreteProxy();

If the ConcreteProxy class is annotated @Proxy, the autogenerated classes can be created at compile time.

Proxy (creational) Yes @CreationalProxy, @ProxyDelegate Annotate proxy class with @CreationalProxy. Methods that can execute without constructing the target object can be annotated @ProxyDelegate. Call AsyncProxy.createAsyncCreationProxy() to obtain a proxy in which invocation of methods not annotated @ProxyDelegate will trigger asynchronous creation of the target object.
Chain of Responsibility No None Handlers extend abstract class CORHandler.
Command Yes @Command (1) Given a command interface ICommand, invoke

CommandMaker maker = AsyncUtil.createAsyncMaker(ICommand.class);

to create a command object factory. Then for any concrete command object myCommand, invoke

ICommand c = maker.make(myCommand);

to create from it an asynchronous command object.

Interpreter No None Ad hoc (see examples)
Iterator No None Use one of the forms of ConcurrentIterator.apply().
Mediator No None Ad hoc (see examples)
Memento X X X
Observer No None Listeners extend abstract class ConcurrentObserver.
State X X X
Strategy Yes @Strategy (1) Given an interface IStrategy and a concrete implementation ConcreteStrategy, replace the call

IStrategy s = new ConcreteStrategy();

with

IStrategy s = AsyncUtil.createAsyncStrategy(IStrategy.class, new ConcreteStrategy());

If the IStrategy interface is annotated @Strategy, the autogenerated classes can be created at compile time.

Visitor No None Visitors extend class AbstractConcurrentVisitor, nodes implement interface VisitableNode.
Template method Yes @Template (1) Given an interface ITemplate, an abstract class AbstractClass that implements it, and a subclass ConcreteClass that implements the abstract methods in AbstractClass, replace the call

ITemplate t = new ConcreteClass();

with

ITemplate t = AsyncUtil.createAsyncTemplate(ITemplate.class, new ConcreteClass());

If the ITemplate interface is annotated @Template, the autogenerated classes can be created at compile time.

(1) Annotations required for compile-time code generation. If annotation is omitted, code will be generated, compiled, and loaded at runtime.