aspect Node { public static final int black = 0; public static final int white = 1; protected int token = white, node = white; abstract protected pointcut sendClass(); abstract protected pointcut sendMethods();protected pointcut sendClass(); abstract protected pointcut sendMethods(); pointcut send(): sendClass() && sendMethods(); after(): send() { node = black; } } aspect Source(MSG METHODS,IDLE,CHILDREN) extends Node issingleton() { protected pointcut sendClass(): within(BOUND CLASS); protected pointcut sendMethods(): call(MSG METHODS); private boolean finished = false; private int numbTokens = 0; declare parents: BOUND CLASS implements SourceIF; public void BOUND CLASS.tokenmsg(int x) throws RemoteException {} //Source’s single advice after(BOUND CLASS C, int x): target(C) && args(x) && execution(void SourceIF.tokenmsg(int)) { if (x == black) token = black; numbTokens++; if (C.CHILDREN.length == numbTokens) { if (!C.IDLE || (node == black) || (token == black)) { token = white; node = white; numbTokens = 0; try { for (int i=0; i < C.CHILDREN.length; i++) { try { ((InternalIF) C.CHILDREN[i]).repeat(); } catch (ClassCastException e) { ((LeafIF) C.CHILDREN[i]).repeat(); } } } catch (Exception e) { System.err.println(”Source err: ” + e.getMessage()); e.printStackTrace(); } } else { finished = true; } } } } aspect Internal(MSG METHODS,IDLE,PARENT,CHILDREN) extends Node perthis(within(BOUND CLASS)) { protected pointcut sendClass(): within(BOUND CLASS); protected pointcut sendMethods(): call(MSG METHODS); private int numbTokens = 0; //Internal’s first advice after(BOUND CLASS C): set(boolean BOUND CLASS.IDLE) && target(C) { checkRule(C); } declare parents: BOUND CLASS implements InternalIF; //Internal’s second advice public void BOUND CLASS.tokenmsg(int x) throws RemoteException {} after(BOUND CLASS C, int x): this(C) && args(x) && execution(void InternalIF.tokenmsg(int)) { if (x == black) token = black; numbTokens++; checkRule(C); } private void checkRule(BOUND CLASS C) { try { if (C.IDLE && (C.CHILDREN.length == numbTokens)) { numbTokens = 0; if (token == white) { try { ((InternalIF) C.PARENT).tokenmsg(node); } catch (ClassCastException e) { ((SourceIF) C.PARENT).tokenmsg(node); } } else { try { ((InternalIF) C.PARENT).tokenmsg(black); } catch (ClassCastException e) { ((SourceIF) C.PARENT).tokenmsg(black); } } token = white; node = white; } } catch (Exception e) { System.err.println(”Internal err: ” + e.getMessage()); e.printStackTrace(); } } public void BOUND CLASS.repeat() throws RemoteException {} //Internal’s third advice after(BOUND CLASS C): execution(void InternalIF.repeat()) && target(C) { try { for (int i=0; i < C.CHILDREN.length; i++) { try { ((InternalIF) C.CHILDREN[i]).repeat(); } catch (ClassCastException e) { ((LeafIF) C.CHILDREN[i]).repeat(); } } } catch (Exception e) { System.err.println(”Internal err: ” + e.getMessage()); e.printStackTrace(); } } } aspect Leaf(MSG METHODS,IDLE,PARENT) extends Node perthis(within(BOUND CLASS)) { protected pointcut sendClass(): within(BOUND CLASS); protected pointcut sendMethods(): call(MSG METHODS); //Leaf’s first advice after(BOUND CLASS C): set(boolean BOUND CLASS.IDLE) && this(C) && !cflow(initialization(BOUND CLASS.new(..))) { checkRule(C); } declare parents: BOUND CLASS implements LeafIF; //Leaf’s second advice public void BOUND CLASS.repeat() throws RemoteException {} after(BOUND CLASS C): execution(void LeafIF.repeat()) && this(C) { token = white; checkRule(C); } private void checkRule(BOUND CLASS C) { try { if (C.IDLE && (token == white)) { try { ((InternalIF) C.PARENT).tokenmsg(node); } catch (ClassCastException e) { ((SourceIF) C.PARENT).tokenmsg(node); } token = black; node = white; } } catch (Exception e) { System.err.println(”Leaf err: ” + e.getMessage()); e.printStackTrace(); } } } FIGURE 3. A superimposition for TerminationDetection. THE COMPUTER JOURNAL, Vol. 46, No. 5, 2003 534 M. SIHMAN AND S. KATZ which is stated in Leaf’s first advice by calling to its checkRule method right after the actual parameter bound to IDLE is assigned a value in the constructor of the bound class. Leaf’s checkRule method may be seen in Figure 3. Leaf’s first advice is shown below: after(BOUND CLASS C): set(boolean BOUND CLASS.IDLE) && this(C) && !cflow(initialization(BOUND CLASS.new(..))) { checkRule(C); } Leaf will send tokenmsg to its parent only when the actual parameter bound to IDLE is set to true. An internal node will pass on a white token only if it has received a white token from all its sons and is itself white, and otherwise will send a black token after receiving tokens from all its sons. This can be seen in the first advice and the checkRule method of Internal (see Figure 3). Internal’s first advice is: after(BOUND CLASS C): target(C) && set(boolean BOUND CLASS.IDLE) { checkRule(C); } If the root is white and has received white tokens from all its sons, then termination has been detected. This check takes place in Source’s single advice, after receiving a tokenmsg, as may be seen in Figure 3. The connection of the basic computation to the superimposition is through the formal parameter IDLE, while the needed spanning tree is encoded into the parameters PARENT and CHILDREN. We assume—in all generic aspects’ condition sections—that if the variable that will be bound to IDLE is true, then the (augmented) basic algorithm in the associated object has no pending internal computation and will only respond to new incoming messages. Thus this termination detection algorithm is especially appropriate for underlying reactive object systems. As may be seen in the global result section, the variable finished in the Source generic aspect indicates when a global passive state has been detected. This superimposition is spectative, because it merely detects termination of basic activities by setting a new variable, but does not change the basic computation. It could be used in conjunction with other superimpositions that announce the problem or actually terminate the basic objects. We use remote method invocation (RMI) to implement message passing between the objects in our examples. We define interfaces SourceIF, InternalIF and LeafIF for the generic aspects Source, Internal and Leaf, respectively; both SourceIF and InternalIF contain method tokenmsg, which treats the receipt of token messages, while InternalIF and LeafIF contain method repeat, to handle the receipt of repeat messages from the root. Each generic aspect introduces— to its bound class—a declaration that the augmented class implements the associated interface and the implementation of the associated remote methods tokenmsg and repeat. This technique may be used whenever we want to introduce new messages to basic classes, often allowing us to increase their parallelism and distribution. A possible activation of sj2aj for superimposition TerminationDetection for a given binding file called bindfile is: sj2aj TerminationDetection bindfile where a simple example of bindfile (with the basic class and its actual parameters in regular font, and the aspect name in slant), is: Basic1 Source(void msg1(),inert,children,ended) Basic2 Internal(int msg2(),unoccupied,up,down) Basic3 Leaf (void msg1() || void msg3(int),inactive,father) Note that in the global condition section the phrase ‘semisynchronous communication’ appears. This is intended to mean that the basic program, as well as the augmented one, must use either synchronous send and receive operations, or at least guarantee that a message will be sensed by the receiving object before an entire wave can traverse the spanning tree. If it did not hold, the algorithm could be shown to be incorrect for basic messages that remain in non-tree channels for an entire round of a token wave and a repeat wave. Since we use RMI for message passing, we are assured that this condition holds, as an object suspends its execution when calling a remote method, which is equivalent to a synchronous communication. The correctness of the superimposition is proven in [19], although the crucial assumption on semi-synchronous communication is expressed somewhat vaguely. 3.2. Dining Philosophers Problem Figure 4 shows a superimposition (DPP-H) that solves the heavy-load case of the Dining Philosophers Problem (DPP) [20] for arbitrary neighbor graphs, where each philosopher immediately wants to eat again after finishing an eating session. This can be visualized using a constraint graph, where each augmented object is represented by a node. Two nodes P and Q are connected if and only if P ∈ NEIGHBORSQ and Q ∈ NEIGHBORSP , where NEIGHBORS is an array containing a node’s neighbor objects, which is a parameter of DPP-H’s only generic aspect (DPP-Heavy). In DPP, a philosopher (object) may eat (execute some critical computation CC) only when it has all its associated forks. Each fork is associated with two philosophers and must be used by only one philosopher at a time. DPP-H ensures—in the global result section— that augmented objects that are neighbors in the constraint graph may not execute their CCs concurrently, as each augmented object will alternate with its neighbors the right to execute its CC. Besides mutual exclusion of CCs among neighbor processes, DPP-H ensures absence of deadlock and starvation (concerning the execution of the CCs). DPP-H requires—in the global condition section—the NEIGHBORS arrays of the basic objects to be consistent, where the (bound) NEIGHBORS arrays are consistent when P ∈ NEIGHBORSQ ⇐⇒ Q ∈ NEIGHBORSP , for THE COMPUTER JOURNAL, Vol. 46, No. 5, 2003 SUPERIMPOSITIONS AND ASPECT-ORIENTED PROGRAMMING 535 superimposition DPP-H { global result mutual exclusion; absence of deadlock and starvation P and Q may execute CC concurrently ⇐⇒ ¬neighbors(P,Q) each object P may execute its CC infinitely often global condition P ∈ NEIGHBORSQ ⇐⇒ Q ∈ NEIGHBORSP aspect DPP-Heavy(EAT METHOD,AN INTERFACE,NEIGHBORS, NEIG IDS,MY ID) perthis(within(BOUND CLASS)); result aug obj P will o
[1]
Michel Wermelinger,et al.
A graph based architectural (Re)configuration language
,
2001,
ESEC/FSE-9.
[2]
Kris De Volder.
Aspect-Oriented Logic Meta Programming
,
1998,
ECOOP Workshops.
[3]
Henny B. Sipma,et al.
Deductive verification of real-time systems using STeP
,
1997,
Theor. Comput. Sci..
[4]
Zohar Manna,et al.
The Temporal Logic of Reactive and Concurrent Systems
,
1991,
Springer New York.
[5]
Olivier Motelet,et al.
A Formal Definition of Crosscuts
,
2001,
Reflection.
[6]
Eli Gafni,et al.
Concurrency in heavily loaded neighborhood-constrained systems
,
1989,
ICDCS.
[7]
Kari Systä,et al.
Object-oriented specification of reactive systems
,
1990,
[1990] Proceedings. 12th International Conference on Software Engineering.
[8]
Shmuel Katz,et al.
A superimposition control construct for distributed systems
,
1993,
TOPL.
[9]
C. Q. Lee,et al.
The Computer Journal
,
1958,
Nature.
[10]
Akinori Yonezawa,et al.
Abstracting Object Interactions Using Composition Filters
,
1993,
ECOOP Workshop.
[11]
K. Mani Chandy,et al.
Parallel program design - a foundation
,
1988
.
[12]
Stanley M. Sutton,et al.
N degrees of separation: multi-dimensional separation of concerns
,
1999,
Proceedings of the 1999 International Conference on Software Engineering (IEEE Cat. No.99CB37002).
[13]
Orna Grumberg,et al.
Network Grammars, Communication Behaviors and Automatic Verification
,
1989,
Automatic Verification Methods for Finite State Systems.
[14]
Matthew B. Dwyer,et al.
Using the Bandera Tool Set to Model-Check Properties of Concurrent Java Software
,
2001,
CONCUR.
[15]
Shmuel Katz,et al.
A calculus of superimpositions for distributed systems
,
2002,
AOSD '02.
[16]
Shmuel Katz,et al.
Aspects and Superimpositions
,
1999,
ECOOP Workshops.
[17]
Rodney W. Topor,et al.
Termination Detection for Distributed Computations
,
1984,
Inf. Process. Lett..
[18]
Kaisa Sere,et al.
Superposition refinement of reactive systems
,
2005,
Formal Aspects of Computing.
[19]
William G. Griswold,et al.
An Overview of AspectJ
,
2001,
ECOOP.
[20]
Gregor Kiczales,et al.
Aspect-oriented programming
,
2001,
ESEC/FSE-9.
[21]
Nissim Francez,et al.
A compositional approach to superimposition
,
1988,
POPL '88.
[22]
Karl J. Lieberherr,et al.
Adaptive object-oriented programming using graph-based customization
,
1994,
CACM.
[23]
Natarajan Shankar,et al.
Formal Verification for Fault-Tolerant Architectures: Prolegomena to the Design of PVS
,
1995,
IEEE Trans. Software Eng..