University of Alabama at Birmingham Vanderbilt University An
University of Alabama at Birmingham Vanderbilt University An Approach for Supporting Aspect-Oriented Domain Modeling GPCE 2003 – Erfurt, Germany September 24, 2003 Jeff Gray, Ted Bapty, Sandeep Neema, Doug Schmidt, Andy Gokhale and Bala Natarajan gray (at) cis. uab. edu http: //www. gray-area. org This research is funded by DARPA/IXO, under the PCES program.
Motivating Problem – Crossccuting Constraints in Real-Time/Embedded Models Changeability? ? ? n Base models become constrained to capture a particular design Multiple Levels of Hierarchy c A B d Replicated Structures F e B n Constraints that are related to some global property are dispersed across the model c d B e Context Sensitive Crosscutting Constraints c d e
Importance of Changeability in Modeling q Modeling’s key advantage: n q Ability to rapidly explore “what-if” design alternatives Changeability a metric for modularity: “The way to evaluate a modular decomposition…is to ask what changes it accommodates. ” David Weiss, chapter preface in Software Fundamentals q Ensure benefit of model-driven approach: “Small changes in requirements entail large changes in the structure and configuration” Gerald Jay Sussman, “Robust Design through Diversity, ” DARPA Amorphous Computing Workshop, 1999.
Motivation Summary Key Problems: q n n Difficult to specify and manage cross-cutting concerns (e. g. , constraints) in model-based systems Lack of tool support for automatically weaving concerns into models Our Solution: q n A meta framework that assists in the construction of model weavers, capable of rapidly dispersing global concerns across a design space; not just notational Constraint-Specification Aspect Weaver http: //www. gray-area. org/Research/C-SAW
Modeling Context: MIC/GME Meta-Model of Stateflow using UML/OCL as meta-modeling language. META-MODEL Model interpreter 1 Model interpreter 2 Matlab n Key focus of this paper: q DOMAIN-MODEL q C++ Weaving of high-level concerns into domain model Framework for creating new weavers for each meta-model Model interpreter 3 FPGA Model instance of Stateflow
Process of Using a Model Weaver GME <? xml version="1. 0" encoding="UTF-8"? > <!DOCTYPE project SYSTEM " mga. dtd"> <project guid="{0000 -0000 -00000000}" cdate ="Thu Nov 30 14: 15: 40 2000" mdate="Thu Nov 30 14: 15: 40 2000" metaguid="{0000 -0000 -00000000}" metaname ="PCES"> <name>bit 1</name> <comment></comment> <author></author> <folder id="id-006 a-00000001" kind=" Root. Folder"> <name>bit 1</name> <folder id="id-006 a-00000002" kind="Structural"> <name>Structural</name> <model id="id-0065 -00000001" kind="Processing. Compound"> <name>Processing. Compound</name> <attribute kind="Description" status="meta"> <value></value> </attribute> <atom id="id-0066 -00000007" kind="Attribute" role="Attrib"> <name> Gates. Per. Bit</name> <regnode name=" Part. Regs"> <value></value> <regnode name=" Structural. Aspect "> <value></value> <regnode name="Position" isopaque="yes"> <value>37, 153</value> </regnode > <attribute kind="Value" status="meta"> <value></value> </attribute> </atom> <atom id="id-0066 -00000006" kind="Attribute" role="Attrib"> <name> Nom. Bits</name> <regnode name=" Part. Regs"> <value></value> <regnode name=" Structural. Aspect "> <value></value> <regnode name="Position" isopaque="yes"> <value>205, 76</value> </regnode > <attribute kind="Value" status="meta"> <value></value> </attribute> </atom> <atom id="id-0066 -00000005" kind="Attribute" role="Attrib"> <name> Max. Bits</name> <regnode name=" Part. Regs"> <value></value> <regnode name=" Structural. Aspect "> <value></value> <regnode name="Position" isopaque="yes"> <value>128, 76</value> </regnode > Enhanced FOO. XML <? xml version="1. 0" encoding="UTF-8"? > <!DOCTYPE project SYSTEM " mga. dtd"> FOO. XML <project guid="{0000 -0000 -00000000}" cdate ="Thu Nov 30 14: 15: 40 2000" mdate="Thu Nov 30 14: 15: 40 2000" metaguid="{0000 -0000 -00000000}" metaname ="PCES"> <name>bit 1</name> <comment></comment> <author></author> <folder id="id-006 a-00000001" kind=" Root. Folder"> <name>bit 1</name> <folder id="id-006 a-00000002" kind="Structural"> <name>Structural</name> <model id="id-0065 -00000001" kind="Processing. Compound"> <name>Processing. Compound</name> <attribute kind="Description" status="meta"> <value></value> </attribute> <atom id="id-0066 -00000007" kind="Attribute" role="Attrib"> <name> Gates. Per. Bit</name> <regnode name=" Part. Regs"> <value></value> <regnode name=" Structural. Aspect "> <value></value> <regnode name="Position" isopaque="yes"> <value>37, 153</value> </regnode > <attribute kind="Value" status="meta"> <value></value> </attribute> </atom> <atom id="id-0066 -00000006" kind="Attribute" role="Attrib"> <name> Nom. Bits</name> <regnode name=" Part. Regs"> <value></value> <regnode name=" Structural. Aspect "> <value></value> <regnode name="Position" isopaque="yes"> <value>205, 76</value> </regnode > <attribute kind="Value" status="meta"> <value></value> </attribute> </atom> <atom id="id-0066 -00000005" kind="Attribute" role="Attrib"> <name> Max. Bits</name> <regnode name=" Part. Regs"> <value></value> <regnode name=" Structural. Aspect "> <value></value> <regnode name="Position" isopaque="yes"> <value>128, 76</value> </regnode > Domain-Specific Weaver Modeling Pointcuts constraint FOOB 2 { // apply a specific constraint to “B 2” only in Structural models("Processing. Compound")-> select(p | p. name() == "B 2")->Power. Strategy(1, 100); } constraint FOOBStar { // apply a specific constraint to all nodes beginning with “B” - use wildcard in Structural models("Processing. Compound")-> select(p | p. name() == "B*")->Power. Strategy(1, 100); }
Quantification Over Base Code An Aspect. J example n Advice Comp 1 package org. apache. tomcat. session; void validate() { // if we have an inactive interval, check to see if // we've exceeded it import org. apache. tomcat. core. *; import org. apache. tomcat. util. String. Manager ; import java. io. *; import java. net. *; import java. util. *; import javax. servlet. http. *; if ( inactive. Interval != -1) { int this. Interval = ( int)(System. current. Time. Millis () - last. Accessed ) / 1000; if ( this. Interval > inactive. Interval ) { invalidate(); /** * Core implementation of a server session * * @author James Duncan Davidson [ duncan@eng. sun. com ] * @author James Todd [gonzo@eng. sun. com] */ Server. Session. Manager ssm = Server. Session. Manager. get. Manager (); ssm. remove. Session(this ); } } public class Server. Session { after(Object o) throwing (Error e): pub. Intf(o) { log. write(o, e); … } } private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session "); private Hashtable values = new Hashtable (); private Hashtable app. Sessions = new Hashtable (); private String id; private long creation. Time = System. current. Time. Millis (); ; private long this. Access. Time = creation. Time ; private long last. Accessed = creation. Time ; private int inactive. Interval = -1; synchronized void invalidate() { Enumeration enum = app. Sessions. keys (); Server. Session(String this. id = id; } while ( enum. has. More. Elements ()) { Object key = enum. next. Element (); Application. Session app. Session = ( Application. Session)app. Sessions. get(key ); app. Session. invalidate (); } id) { } public void put. Value(String name, Object value) { if (name == null) { String msg = sm. get. String("server. Session. value. iae "); public String get. Id () { return id; } throw new Illegal. Argument. Exception(msg ); } public long get. Creation. Time () { return creation. Time ; } remove. Value(name ); // remove any existing binding values. put(name, value); } public long get. Last. Accessed. Time () { return last. Accessed ; } public Object get. Value(String name) { if (name == null) { String msg = sm. get. String("server. Session. value. iae "); public Application. Session get. Application. Session(Context boolean create) { Application. Session app. Session = ( Application. Session)app. Sessions. get(context ); context, throw new Illegal. Argument. Exception(msg ); } return values. get(name); if ( app. Session == null && create) { } // XXX // sync to ensure valid? public Enumeration get. Value. Names () { return values. keys(); } app. Session = new Application. Session(id , this, context); app. Sessions. put(context , app. Session ); public void remove. Value(String values. remove(name); } } // XXX // make sure that we haven't gone over the end of our // inactive interval -- if so, invalidate and create // a new app. Session name) { public void set. Max. Inactive. Interval(int inactive. Interval = interval; } return app. Session ; interval) { public int get. Max. Inactive. Interval () { return inactive. Interval ; } } void remove. Application. Session(Context app. Sessions. remove(context ); } context) { // XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later. /** * Called by by context when request comes in in so so that accesses and * inactivities can be dealt with accordingly. */ synchronized void reap() { Enumeration enum = app. Sessions. keys (); void accessed() { // set last accessed to this. Access. Time as it will be left over // from the previous access while ( enum. has. More. Elements ()) { Object key = enum. next. Element (); Application. Session app. Session = ( Application. Session)app. Sessions. get(key ); last. Accessed = this. Access. Time ; this. Access. Time = System. current. Time. Millis (); app. Session. validate (); } } void validate() Comp 2 // XXX // sync'd for safty -- no other thread should be getting something // from this while we are reaping. This isn't the most optimal // solution for this, but we'll determine something else later. package org. apache. tomcat. session; import org. apache. tomcat. util. *; import org. apache. tomcat. core. *; import java. io. *; import java. net. *; import java. util. *; import javax. servlet. http. *; synchronized void reap() { Enumeration enum = sessions. keys(); while ( enum. has. More. Elements ()) { Object key = enum. next. Element (); Server. Session session = ( Server. Session)sessions. get(key ); /** * * @author James Duncan Davidson [ duncan@eng. sun. com ] * @author Jason Hunter [ jch@eng. sun. com ] * @author James Todd [gonzo@eng. sun. com] */ public class Server. Session. Manager session. reap(); session. validate(); } } implements Session. Manager { synchronized void remove. Session(Server. Session String id = session. get. Id (); private String. Manager sm = String. Manager. get. Manager("org. apache. tomcat. session "); private static Server. Session. Manager manager; // = new Server. Session. Manager (); session) { session. invalidate(); sessions. remove(id); } protected int inactive. Interval = -1; static { manager = new public void remove. Sessions(Context context) { Enumeration enum = sessions. keys(); Server. Session. Manager (); } public static Server. Session. Manager return manager; while ( enum. has. More. Elements ()) { Object key = enum. next. Element (); Server. Session session = ( Server. Session)sessions. get(key ); Application. Session app. Session = session. get. Application. Session(context , false); get. Manager () { } private Hashtable sessions = new Hashtable (); private Reaper reaper; if ( app. Session != null) { app. Session. invalidate (); } private Server. Session. Manager () { reaper = Reaper. get. Reaper (); reaper. set. Server. Session. Manager(this ); reaper. start(); } } } /** * Used by context to configure the session manager's inactivity timeout. * * The Session. Manager may have some default session time out, the * Context on the other hand has it's timeout set by the deployment * descriptor ( web. xml ). This method lets the Context conforgure the * session manager according to this value. * * @ param minutes The session inactivity timeout in minutes. */ public void set. Session. Time. Out(int minutes) { if(-1 != minutes) { // The manager works with seconds. . . inactive. Interval = (minutes * 60); } } public void accessed( Context ctx, Request req, String id ) { Application. Session ap. S=(Application. Session)find. Session ( ctx, id); if( ap. S==null) return; Server. Session serv. S =ap. S. get. Server. Session (); serv. S. accessed (); ap. S. accessed (); // cache it - no need to compute it again req. set. Session ( ap. S ); } public Http. Session create. Session(Context ctx) { String session. Id = Session. Id. Generator. generate. Id (); Server. Session session = new Server. Session(session. Id ); sessions. put(session. Id , session); if(-1 != inactive. Interval ) { session. set. Max. Inactive. Interval(inactive. Interval pointcut pub. Intf(Object o): call(public * com. borland. *. *(. . )) && target(o); } return session. get. Application. Session ( ctx, true ); } public Http. Session find. Session(Context ctx, String id) { Server. Session s. Session =(Server. Session)sessions. get(id ); if(s. Session ==null) return null; return s. Session. get. Application. Session(ctx , false); } Pointcut
Quantification Over a Domain Model n Apply AO Weaving concepts to Model-based systems q Weavers ‘Decorate’ Models with attributes & constraints q Weavers compose new model constructs Model Domain-specific Strategies Strategy 1 Strategy 2 Strategy 3 Strategy. N … select(p | p. name() == “Model*” && p. kind() == “State. Flow”)->Strategy 3(); … Modeling Pointcut
Constructing Model Weavers
Domain-Specific Weavers General Motors Factory + GM specific weaver Du. Pont Chemical Factory + Du. Pont specific weaver Boeing Bold Stroke + Bold Stroke specific weaver Example: Evidence of “meta” in the corresponding XML of each of the above models
The Metaweaver Framework XML Modeling Pointcuts Strategies (C++) (Model Hierarchy) Strategies strategy Apply. Constraint(constraint. Name: string, expression : string) { add. Atom("OCLConstraint", "Constraint", constraint. Name). add. Attribute("Expression", expression); Strategy Code Generator } strategy Remove. Constraint(constraint. Name: string) { find. Atom(constraint. Name). remove. Child(); } strategy Replace. Constraint(constraint. Name: string, expression : string) { Remove. Constraint(constraint. Name); Apply. Constraint(constraint. Name, expression); }
Embedded Constraint Language Included OCL Operators Arithmetic Operators Logical Operators Collection Operator Property Operator Standard OCL Collection Operators +, -, *, /, =, <, >, <=, >=, <> and, or, xor, not, implies, if/then/else -> . collection->size() : integer collection->for. All( x | f(x) ) : Boolean collection->select( x | f(x) ) : collection->exists( x | f(x) ) : Boolean
Embedded Constraint Language n n n Traditional OCL has been strictly a declarative query language New uses require an imperative procedural style Addition of side effects into model q Examples: n n n add. Atom(…), find. Atom(…) add. Attribute(…), find. Attribute(…) remove. Node(…) Support for recursion Chaining of strategies (procedure calls) q Inlined C++ code
List of ECL Operators Aggregates folders, models, atoms, attributes, connections Connections connpoint, target, refs, resolve. Refeered. ID, resolve. IDReferred Transformation add. Attribute, add. Atom, add. Model, add. Connection, remove. Node Selection find. Folder, find. Model, find. Atom, find. Attribute. Node General id, parent, get. ID, get. Int, get. Str
Example: Processor Assignment n Weapons deployment Sensor WCET=1 ms WCET=4 ms 100 Hz Latency < 2 ms Latency < 10 ms Update Σ (x, y, z) Map. DB Weapon Release WCET=2 ms Latency < 5 ms WCET=150 ms WCET=1 ms Latency < 20 ms (x, y, z) Display
Processor Assignment: Component Interaction Model
Processor Assignment: Component Internals
Processor Assignment: Modeling Pointcut aspect Processor. Assignment { models("")->select(m | m. kind() == “Comp*")->Assign(10); }
Processor Assignment: Strategy strategy Assign(limit : integer){ declare static accumulate. WCET, process. Num : integer; declare current. WCET : integer; self. compute. WCET. get. Int(current. WCET); accumulate. WCET : = accumulate. WCET + current. WCET; if (limit < accumulate. WCET) then accumulate. WCET : = current. WCET; process. Num : = process. Num + 1; endif; <<CCom. BSTR a. Constraint = "self. assign. To()=processor" + XMLParser: : itos(process. Num); >> Add. Constraint("Process. Constraint", a. Constraint); }
Processor Assignment: Weaved Constraint self. assign. To() = processor 0 strategy Assign(limit : integer){ declare static accumulate. WCET, process. Num : integer; declare current. WCET : integer; self. compute. WCET. get. Int(current. WCET); accumulate. WCET : = accumulate. WCET + current. WCET; if (limit < accumulate. WCET) then accumulate. WCET : = current. WCET; process. Num : = process. Num + 1; endif; <<CCom. BSTR a. Constraint = "self. assign. To()=processor" + XMLParser: : itos(process. Num); >> Add. Constraint("Process. Constraint", a. Constraint); }
Code Generation Example n Consider the following, which appears in an Eager. Lazy strategy: … components. models()->select(c | c. id() == ref. ID)->eager. Lazy(…); …
Code Generation Example CCom. Ptr<IXMLDOMNode. List> models 0 = XMLParser: : models(components, ""); node. Type. Vector select. Vec 1 = XMLParser: : Convert. Dom. List(models 0); node. Type. Vector select. Vec. True 1 = new std: : vector<node. Type>; vector<node. Type>: : iterator itr. Select 1; for(itr. Select 1 = select. Vec 1 ->begin(); itr. Select 1 != select. Vec 1 ->end(); itr. Select 1++) { node. Type select. Node 1 = (*itr. Select 1); node. Type c; c = select. Node 1; CCom. BSTR id 0 = XMLParser: : id(c); Cl. Data varforward 1(id 0); Cl. Data varforward 2(referred. ID); bool varforward 3 = varforward 1 == varforward 2; if(varforward 3) select. Vec. True 1 ->push_back(*itr. Select 1); } vector<node. Type>: : iterator itr. Coll. Call 1; for(itr. Coll. Call 1 = select. Vec. True 1 ->begin(); itr. Coll. Call 1 != select. Vec. True 1 ->end(); itr. Coll. Call 1++) eager. Lazy: : apply(…);
Sample XMLParser Methods node. Type XMLParser: : add. Atom(node. Type self, CCom. BSTR kind, CCom. BSTR role, CCom. BSTR name) { return add. Node(self, "atom", kind, role, name); } node. Type XMLParser: : find. Model(node. Type a. Node, CCom. BSTR name) { CCom. BSTR bstr. Find(L". /model[name=""); bstr. Find. Append(name); bstr. Find. Append(""]"); return submit. XPath(a. Node, bstr. Find); } CCom. BSTR XMLParser: : id(node. Type a. Node) { CCom. BSTR res; CCom. Ptr<IXMLDOMNode> attr = XMLParser: : find. Attribute (a. Node, "id"); XMLParser: : get. Str(attr, res); return res; }
Summary Domain-Specific Strategies strategy Apply. Constraint(constraint. Name: string, expression : string) { n Domain-specific strategies (encoded in a DSL) are used to instantiate a new model weaver n Specification aspects and base model are sent through the weaver add. Atom("OCLConstraint", "Constraint", constraint. Name). add. Attribute("Expression", expression); } strategy Remove. Constraint(constraint. Name: string) Apply. Constraint(constraint. Name, expression); } Meta-weaver Framework Strategies (C++) Domain-specific Models Modeling Pointcuts B constraint FOOB 2 { // apply a specific constraint to “B 2” only in Structural models("Processing. Compound")-> // apply a specific constraint to all nodes beginning with “B” - use wildcard in Structural models("Processing. Compound")-> select(p | p. name() == "B*")->Power. Strategy(1, 100); } c 3 1 c B 2 d e Constrained Models New n The weaver distributes constraints across the base model Two-level aspect weaving (see upcoming AOSD book)
Summary Benefits of this Approach n The modeler can now perform various “what-if” scenarios using modeling constraints q q n n Impossible in previous approach Constraints can be plugged/unplugged in model Because much of the redundancy of constraint application is removed, the effect of each constraint on the global system can be better understood. This localization of constraints improves modular reasoning. Allows for the rapid construction of new domainspecific weavers q q Strategies are specified using a DSL Generated code is at a much lower level
For more information n Please give us another week to upgrade site: q gray (at) cis. uab. edu q http: //www. gray-area. org/Research/C-SAW n GME (freely available): q http: //www. isis. vanderbilt. edu/Projects/gme/ n OMG MIC PSIG: q http: //mic. omg. org
A Concluding Quote “Even for this let us divided live…That by this separation I may give that due to thee which thou deservest alone. ” William Shakespeare, Sonnet XXXIX
Extra slides
Tool Independence n n Requires an exposed API for accessing internal model data structures Tool-specific adapters written for each new supported tool Rose Cadena GME Meta. Edit
Adaptive Core Weaving Engine
Not unlike Aspect. J AJDT Eclipse AJDT JBuilder emacs Netbeans/FORTE
- Slides: 31