690 likes | 699 Views
This example showcases the use of three aspects (ShowReadWriteAccess, InstanceLogging, and AutoReset) with three classes (Point, Line, and Rectangle) for aspectual composition.
E N D
Aspectual ComponentsPart 2 April 5, 1999
Composition example • Use three aspects simultaneously with three classes. • Three aspects: • ShowReadWriteAccess • InstanceLogging • AutoReset • Three classes: Point, Line, Rectangle
Weaved Code AutoReset Shapes (Point, Line, Rectangle) ShowReadWriteAccess Point Line InstanceLogging Rectangle
Inheritance between components component ShowReadWriteAccess extends ShowReadAccess { participant DataToAccess { expect void writeOp(Object[] args); replace void writeOp(Object[] args){ System.out.println( "Write access on " + this.toString()); expected(args);}} }
InstanceLogging component(first part) component InstanceLogging { participant DataToLog { expect public DataToLog(Object[] args); replace public DataToLog(Object[] args) { expected(args); long time = System.currentTimeMillis(); try { String class = this.class.getName() + " "; logObject.writeBytes(""New instance of " + class + at "" " + time + "" " \n"); } catch (IOException e) {System.out.println(e.toString());} } }
InstanceLogging component(second part) protected DataOutputStream logObject = null; public init() { try {logObject = new DataOutputStream( new FileOutputStream(log));} catch (IOException e) {System.out.println(e.toString());} } }
AutoReset component component AutoReset { participant DataToReset { expect void setOp(Object[] args); expect void reset(); protected int count = 0; replace void setOp(Object[] args) { if ( ++count >= 100 ) { expected(args); count = 0; reset(); }} } }
Composition of components connector CompositionConn1 { {Line, Point} is ShowReadWriteAccess.DataToAccess with { readOp = get*; writeOp = set*;}; Point is AutoReset.DataToReset with { setOp = set*; void reset() { x = 0; y = 0; } }; {Line, Point, Rectangle} is InstanceLogging.DataToLog;}
Weaved Code AutoReset Shapes ShowReadWriteAccesses Point Line InstanceLogging Rectangle
Composition of components Connector graph CompositionConn1 Line, Point, Rectangle ShowReadWriteAccess.DataToAccess * * AutoReset.DataToReset * InstanceLogging.DataToLog * * *
Modified composition connector CompositionConn2 extends CompositionConn1 { Line is AutoReset.DataToReset with { setOp = set*; void reset() {init();} }; }
Composition of components Connector graph CompositionConn1 Line, Point, Rectangle ShowReadWriteAccess.DataToAccess * * AutoReset.DataToReset * InstanceLogging.DataToLog * * * Connector graph CompositionConn2 Line, Point, Rectangle ShowReadWriteAccess.DataToAccess * * AutoReset.DataToReset * * InstanceLogging.DataToLog * * *
Modify existing connection statements connector CompositionConn3 extends CompositionConn1 { Point is AutoReset.DataToReset with { { setOp = set; void reset() { x = 0; y = 0; }} { setOp = setX; void reset() { x = 0;}} { setOp = setY; void reset() { y = 0;}} }; }
Composition of components Connector graph CompositionConn3 Line, Point, Rectangle ShowReadWriteAccess.DataToAccess * * AutoReset.DataToReset *** InstanceLogging.DataToLog * * * overridden: ***
New example: Feature-oriented Programming • Dependent aspects • Order of deployment is relevant
DataWithCounter componentpairwise interaction Data/Counter component DataWithCounter { private participant Counter { int i=0; void reset(){i=0;}; void inc(){…}; void dec(){…};} participant DataStructure { protected Counter counter; expect void initCounter(); expect void make_empty(); expect void push(Object a); expect void pop(); replace void make_empty(){counter.reset();expected();} replace void push(Object a){counter.inc(); expected(a);} replace void pop() {counter.dec();expected();} } }
DataWithLock Componentpairwise interaction Data/Lock component DataWithLock { participant Data { Lock lock; expect void initLock(); expect AnyType method_to_wrap(Object[] args); replace AnyType method_to_wrap(Object[] args) { if (lock.is_unlocked()) { lock.lock(); expected(Object[] args); lock.unlock(); }}} private participant Lock {boolean l = true; void lock(){…}; void unlock(){…}; boolean is_unlocked(){return l};}
DataWithCounter StackImpl Counter QueueImpl Lock DataWithLock
First connector connector addCounter&Lock { StackImpl is DataWithCounter.DataStructure with { void initCounter() {counter = new Counter();} void push(Object obj) {push(obj));} // use name map instead Object top() {return top();} ... } is DataWithLock.Data with { method_to_wrap = {pop, push, top, make_empty, initCounter}; }; QueueImpl is DataWithCounter.DataStructure with { ... } is DataWithLock.Data with { ... }; }
DataWithCounter DataWithLock DataWithCounter&Lock
Create composed aspects prior to deployment component DataWithCounterAndLock { participant Data = DataWithCounter.DataStructure is DataWithLock.Data with { method-to-wrap = {make_empty, pop, top, push}}; }
Second connector: Deploy composed component connector addCounter&Lock { StackImpl is DataWithCounterAndLock.Data with { void make_empty() {empty();} void initCounter() { counter = new Counter();} void push(Object obj) {push(obj);} ... }; QueueImpl is DataWithCounterAndLock.Data with {...}; }
Defining New Behavior: The Publisher-Subscriber Aspect an aspect can be multiply deployed with the same application, each deployment with its own mappings.
Publisher component PublisherSubscriberProtocol { participant Publisher { expect void changeOp(Object[] args); protected Vector subscribers = new Vector(); public void attach(Subscriber subsc) { subscribers.addElement(subsc);} public void detach(Subscriber subsc) { subscribers.removeElement(subsc);} replace void changeOp() { expected(); for (int i = 0; i < subscribers.size(); i++) {((Subscriber)subscribers.elementAt(i)). newUpdate(this);}}
Subscriber participant Subscriber { expect void subUpdate(Publisher publ); protected Publisher publ; public void newUpdate(Publisher aPubl) { publ = aPubl; subUpdate(publ);} } } }
Class for deployment class ChangePrinter { void public printR() { System.out.println("Printer: " + this.toString() + " read access has occurred ..." + \n); } void public printW() { System.out.println("Printer: " + this.toString() + " write access has occurred ..." + \n); } void public notifyChange() { System.out.println("CHANGE ..."); } }
Deployment 1 connector PubSubConn1 { Point is Publisher with { changeOp = {set*, get*};} ChangePrinter is Subscriber with { void subUpdate(Publisher publ) { notifyChange(); System.out.println(”on Point object " + ((Point) publ).toString()); } } }
Deployment 2 connector PubSubConn2 { TicTacToe is Publisher with { changeOp = {startGame, newPlayer, putMark, endGame}}; {BoardDisplay, StatusDisplay} is Subscriber with { void subUpdate(Publisher publ) { setGame((Game) publ); repaint(); } }; }
Deployment/write connector PubSubConn3 { Point is Publisher with { changeOp = set*;} ChangePrinter is Subscriber with { void subUpdate(Publisher publ) { printW(); System.out.println("on point object " + ((Point) publ).toString()); } } }
Deployment/read connector PubSubConn4 { Point is Publisher with { changeOp = get*;} ChangePrinter is Subscriber with { void subUpdate(Publisher publ) { printR(); System.out.println("on point object " + ((Point) publ).toString()); } } }
Overlap between connectors The sets of operations of Point that are mapped to different notification operations of the subscriber participant need not be disjoint. For instance, we may want to distinguish between set operations that affect the x-coordinate, respectively, the y-coordinate of a point. The set(int, int), however, will then fall in both categories. This is expressed by the connectors PubSubConn3_1 and PubSubConn3_2 below.
Deployment/write connector PubSubConn3_1 { Point is Publisher with { changeOp = {set,setX};} ChangePrinter is Subscriber with { void subUpdate(Publisher publ) { printW(); System.out.println("on point object " + ((Point) publ).toString()); } } }
Deployment/write connector PubSubConn3_2 { Point is Publisher with { changeOp = {set, setY};} ChangePrinter is Subscriber with { void subUpdate(Publisher publ) { printW(); System.out.println("on point object " + ((Point) publ).toString()); } } }
Mapping Participant Graphs • Is the deployment of a component giving the intended result? • Example: Three participants: A, B, C • A has 0..* B; B has 1..* C. • A::f(int x1){for each b: f(x1);} • B::f(int x1){for each c: f(x);} // x a data member local to B • C::f(int x1){print(“at C: number at previous B”); print(x1);}
Expected output at C: number at previous B 78 at C: number at previous B 8
Mapping A B C 0..* 1..* A C B
Refinement This property must hold between a PG and a corresponding CG or another PG. The intent of the refinement relation is to ensure that the behavior in the component will be properly instantiated at the place of use without ``surprising'' behavior.
G1refinementG2 F F D D E E B B C C G2 refinement: connectivity of G2 is in pure form in G1 Allows extra connectivity. G1 A A
G1refinementG2 F F D D E E B B C C G2 G1 refinement: connectivity of G2 is in pure form in G1 A A
G1compatibleG2 F F D D E E B B C C G2 Compatible: connectivity of G2 is in G1 G1 A A
G1strong refinementG2 F F D D E E B B C C G2 refinement: connectivity of G2 is in pure form in G1 and G1 contains no new connections in terms of nodes of G2 G1 A A
Key concepts: refinement • Let G1=(V1,E1) and G2=(V2,E2) be directed graphs with V2 a subset of V1. Graph G1 is a refinement of G2 if for all u,v in V2 we have that (u,v) in E2 implies that there exists a path in G1 between u and v which does not use in its interior a node in V2. • Polynomial time.
Refinement • For each edge in G2 there must be a corresponding pure path in G1. • Pure path = in interior no nodes of G2. • Refinement = strong refinement with “if and only if” replaced by “implies”.
G1refinementG2 F F D D E E B B C C G2 Implementation: create strategy constraint map: bypassing all nodes G1 A A
Refinement means: no surprises not G1 refinement G2 B C C B G2 A G1 A
Refinement means: no surprises G1 refinement G2 B C C B X G2 A G1 A
Refinement means: no surprises not G1 refinement G2 B C B C G2 G1 A A
Alternative definition a graph G is a refinement of a graph S, if S is a connected subgraph of the pure transitive closure of G with respect to the node set of S.
Pure transitive closure • The pure transitive closure of G=(V,E) with respect to a subset W of V is the graph G*=(V,E*), where E*={(i,j): there is a W-pure path from vertex i to vertex j in G}. • A W-pure path from i to j is a path where i and j are in W and none of the inner nodes of the path are in W.
Implementation issues • Translate to AspectJ: requires source code access. • What if aspectual components only in binary? • Want separate compilation of application and aspectual components.