480 likes | 627 Views
Applying Aspect-Oriented Software Development to Middleware Frameworks. Tal Cohen Joint work with Yossi Gil. Research Overview. The problem : simplifying the design, development and modularity of enterprise applications . Huge software programs for banks, government agencies, etc.
E N D
Applying Aspect-Oriented Software Development to Middleware Frameworks Tal Cohen Joint work with Yossi Gil
Research Overview • The problem: simplifying the design, development and modularity of enterprise applications. • Huge software programs for banks, government agencies, etc. • The research outcome: a set of programming language constructs. • Applicable to almost any software development domain. • The problem:simplifying the design, development and modularity of enterprise applications. • Huge software programs for banks, government agencies, etc. • The research outcome: a set of programming language constructs. • Applicable to almost any software development domain. begin::Overview Overview Problem Solution More Results
Contributions • Shakeins – aspect-like mechanism combining AOP with OOP. • AspectJ2EE – a proof-of-concept design for shakeins in Enterprise Java. • Factories – a language-level mechanism for managing object instantiation. • JTL –a language for Query-by-Example searches in a program source. • Object Evolution – objects can change type at runtime in type-safe systems. Overview Problem Solution More Results
Outline • The Problem: Enterprise Application Development. • "The unbelievable complexity of simple chores" • Overview of existing solutions: middleware frameworks, aspects… • … and why they aren't good enough • The Solution: Shakeins • How to integrate AOP with middleware frameworks • Additional Contributions • From better static pointcuts to dynamic shakeins Overview Problem Solution More Results
shakein "Secure" Shakeins Now with anti-theft mechanism! class Secure<Car> class Car Overview Problem Implementation: Engine and other internals Interface remains unchanged Interface: externally visible features Solution More Results
shakein "Logging" Shakeins Now with recording black-box! class Logging<Secure<Car>> We also do buses! Overview Problem Interface is still unchanged Solution More Results
Shakeins in Brief • Intelligently modify the original class: • Can be used to create objects, but not to declare variables (not a new type): Account accnt = new Secure<Account>() • Similar to but less chaotic than aspects. end:: shakein Secure { // Secure<T> re-implements T before execution of any public method (in T) { … perform security test … } } Overview Mixins, Java generics, and C++ templates cannot do this! Problem Solution More Results
Why Programming a Banking Application is Easy begin::Problem class Account { void transferTo(Account other, int amount) { if (balance < amount) throw new OverdraftException(); this.balance -= amount; other.balance += amount; } … Overview Problem Solution More Results
Why Programming a Banking Application is Not Easy class Account { void transferTo(Account other, int amount) { Log.log("Attempting transfer"); User user = Security.getContext().getCurrentUser(); if (!user.canTransferFrom(this)) Log.securityError(); throw new SecurityException(); Transaction tx = Db.getConnection().beginTransaction(); this.refreshFields(); other.refreshFields(); if (balance < amount) tx.rollback(); throw new OverdraftException(); this.balance -= amount; other.balance += amount; this.updateDb(); other.updateDb(); tx.commit(); Log.log("Transfer completed"); } … LOGGING SECURITY Overview Problem BUSINESS LOGIC Solution PERSISTENCE TRANSACTION MANAGEMENT More Results Non-functional concerns yield tangled and scattered code.
Making EnterpriseDevelopment Easier • Past: Work hard • Bonus: You get to use COBOL • Present: Middleware Frameworks • CORBA, DCOM, Enterprise Java • Future: Aspect-Oriented Middleware Frameworks • Past • Present • Future Overview Problem Solution More Results
Middleware Frameworksand Services • Enable large-scale, multi-tier application development. • Provide services attachable to business logic. • The (unreached) ideal: • You write the business logic, • The framework handles everything else. • Services are configurable. • e.g., by XML files. Overview Problem Solution More Results
Programming a Banking Application in J2EE • Set of services is pre-defined and not extensible. • So (e.g.) logging remains tangled and scattered. class AccountBean { void transferTo(Account other, int amount) { Log.log("Attempting transfer"); if (getBalance() < amount) throw new OverdraftException(); setBalance(getBalance() – amount); other.setBalance(other.getBalance() + amount); Log.log("Transfer completed"); } … LOGGING BUSINESS LOGIC Overview • Not trivial to use. • e.g., can't simply access the field balance. • Several support class required (not shown) – home, interface, etc. • Somewhat easier with v5. Problem <ejb-jar> <enterprise-beans> <entity> <ejb-name>Account</ejb-name> <home>come.acme.AccountHome</home> <remote>com.acme.Account</remote> <ejb-class>com.acme.AccountBean</ejb-class> <persistence-type>Container</persistence-type> … Persistence, transactions, security: services, configured using XML files. Solution • Services arenot programmable. • Need a different security model? No can do. • Can't control how persistence logs operations. More Results
AOP Aspect-Oriented ProgrammingTo The Rescue! • The new kid in town: a buzzword and a panacea solution to tangled and scattered code: • Decompose the system into distinct aspects (code modules). • Develop each aspect independently. • The aspects are then woven into a complete system. • Chief example: AspectJ Overview Problem Solution More Results
Decomposition and Weaving Clearly distinct concerns Executable Overview Problem Program requirements Aspect Weaving (mechanical process) Aspectual Decomposition (conceptual process) Solution More Results
Programming a Banking Application with AOP • Very extensible. • Define new aspects as you deem fitting. class Account { void transferTo(Account other, int amount) { if (balance < amount) throw new OverdraftException(); this.balance –= amount; other.balance += amount; } … BUSINESS LOGIC • Highly programmable. • Not just a pre-defined set of code transformations. Overview aspect Security { before execution of any public method in Account { User user = Security.getContext().getCurrentUser(); if (!user.canAccessAccount(this)) throw new SecurityException(); } } Everything else: defined as Aspects Problem aspect Logging { before execution of public methods in Account { Log.log("Began " + methodName); } after successful execution of same { Log.log(methodName + " ended successfully"); } after failed execution of same { Log.log(methodName + " caused exception: " + e); … Solution • Reusable and configurable. • e.g., the same logging aspect can be used for many classes. More Results
The Problem with Aspects • Aspects are very similar to inheritance in that they create variations on existing code. • However, there is a schism between aspects and inheritance. • A "paradigm mismatch" between AOP and OOP. • Worse, aspects do not scale. • Can't handle large enterprise applications. Overview Problem Solution More Results
Aspects as Class Modifiers • Unclear: How may an aspect modify the type? • Can it remove members? • Can it change method signatures? • Unclear: How may an aspect modify the instance creation mechanisms? • Can it require additional constructor parameters? • Insert code before super()? • Unclear: Does an aspect modify subclasses, too? • Does a subclass inherit from the modified version of the class, or from the original one? • Does an advice applied to "before execution of method m()" apply to execution of overridden versions of m, too? Overview Problem Solution More Results
Scalability? • Versatility • AOP: Aspect application is destructive. • Enterpriseapps: Legacy code must not be broken. • Plus, different modifications of same base class required by different parts of the program. • Configurability • AOP: Precedence problems with multiple aspects. • Enterpriseapps: Order matters – and should not be set globally. Overview Problem Solution More Results
Manageability? • Granularity • AOP: An aspect may apply to any class! • Enterpriseapps: Selective application required. • Seeing the Big Picture • AOP: Class is oblivious to aspects that modify it • Enterpriseapps: Need to know what aspects apply to a given class Overview Problem Solution More Results
Aspects Do Not Scale • The AspectJ philosophy: • Throw in all program modules (classes, aspects). • Let the compiler do the mixing. end:: Overview Problem Solution More Results
Shakeins • The basic principle: Use OOP to implement AOP. • Use the existing inheritance mechanism to implement class modifications. • Suddenly, all the problems disappear. • No paradigm mismatch… begin::Solution Overview Problem Solution More Results
What is a Shakein? • A Shakein makes a re-implementation of a class. • It does not change the type. • We get a new implementation of an existing type. • The old implementation continues to exist! • A parameterized, generic-like structure. Overview Problem Solution More Results
Shakeins as “Reimplements” Operators A class in OOP is: • A type declaration C p; • Template for object creation (constructor, field layout etc.) p = new C(); • An implementation p.m(); Simple subclassing: • Creates a subtype by adding more functions to the protocol. • Extends the structure definition by adding more fields. • Modifies the behavior by replacement and refinement Given a class C, shakein S, the application C’=S[p1,…,pn]<C> is a new class such that • C’ has the same type as C. • Cannot declare variables of type C’ • May have additional fields. • May have a modified behavior. Overview Problem Solution More Results
Aren't These Just Mixins? • A mixin takes a class parameter and generates a subclass. • M<C> extends C. • Encapsulates the "delta" between layers of inheritance. • However, unlike shakeins, mixins: • Create a new type. • Must be highly aware of the superclass. • Can't include instructions to "add security tests to every public method". • Unparameterized (except for the superclass). Overview Problem Solution More Results
Shakein Example Note: not "public methods in Account" Implicit type parameter ("<T>") Overview shakein Logging { before execution of public methods { Log.log("Began " + methodName); } after successful execution of same { Log.log(methodName + " ended successfully"); } after failed execution of same { Log.log(methodName + " caused exception: " + e); … Problem Solution More Results
Using a Shakein • Account accnt = new Logging<Account>(); • The shakein application is explicit. • The variable type is the (original) type. • Shakeins do not define new types, only new implementations! Overview Problem Solution More Results
Applying a Shakein to a Class Hierarchy Type C1 C1 C1’ Overview Type C2 Type C3 C2 C2’ C3 C3’ Problem Solution Type C4 No inheritance relationship between the re-implementations C4 C4’ More Results Since no new types are defined, this does not disturb polymorphism!
Shakeins Do Scale • Explicit application – no undesired classes are matched, ever. • Original class still exists – existing code is not broken. • If the changes are desired in existing code, only instance creation points have to be changed. • See our work on factories… • Explicit and flexible ordering – you can have Logging<Secure<Account>>, or Secure<Logging<Account>>, or both. Overview Problem Solution More Results
Parameterization • What if we want different instances of Account to log into different log files? • With aspects – though luck. • With shakeins – just add a parameter to the shakein: Overview Problem shakein Logging[String filename] { before execution of public methods { Log.log(filename, "Began " + methodName); } after successful execution of same { Log.log(filename, methodName + " ended"); } … Solution More Results Account a = new Logging["a.log"]<Account>(); Account b = new Logging["b.log"]<Account>();
Repeated Applications • We can even apply the same shakein more than once to the same class. • Possibly with different parameters. • Example: Log["pre"]<Secure<Log["post"]<Account>>>() Overview Log of operations after security tests. Problem Solution Log of operations before security tests. More Results
Pointcut Parameters • A "pointcut" is the expression that specifies where the aspect/shakein code ("advice") should be injected. • Examples: "execution of public methods", "read access of field f", etc. • Shakeins support pointcut parameters. Overview Problem Solution shakein Secure[pointcut p, String role] { before p { SecurityContext().requireRole(role); } } More Results
Pointcut Parameters + Repeated Application Secure[Pt, "teller"]< Secure[Pc, "client"]<Account>> • Pt = execution of createNew || changeOwner • (teller-only operations) • Pc = execution of transferTo || withdraw • (client operations) Overview Problem Solution More Results
Shakein Composition shakein StdPersistence = Transactional ○ Persistent; shakein MySecure = Secure[Pt, "teller"] ○ Secure[Pc, "client"]; Overview Problem Solution More Results
Other Middleware AOP Solutions • JBoss AOP: Extension to the JBoss open-source J2EE server. • Works by bytecode manipulation. • Supports runtime application/removal of aspects. • Susceptible to runtime failure if not configured properly. • Spring AOP: Open-source "lightweight" middleware framework. • Works using interceptors. • Pointcuts evaluated at runtime. Overview Problem Solution More Results
Feature Comparison Overview Problem Solution More Results
Performance Comparison • Given a class Point, apply aspects (shakeins) to confine the valid range of values for x and y. • Classic example of repeated, parameterized application. • Code in all 3 versions optimized for repeated application (no code duplication) rather than performance. Overview Problem Solution More Results
How Do You Take Your Aspects? Shaken, not stirred. end:: Overview Problem Solution More Results
AspectJ2EE • An integration of the shakein concept into existing J2EE frameworks and applications. • Each service defined as a shakein. • Shakein application managed by XML configuration files. • No expressions like Secure[Pt, "teller"]<Secure[Pc, "client"]<Account>> appear in the code. • J2EE Home objects ensure that all instances are created with shakeins applied. • Deploy-time weaving. Overview begin::More Results Problem Solution More Results
JTL: The Java Tools Language • Joint research with Itay Maman and Evelina Zarivach. • The AspectJ pointcut-specification sublanguage is generally viewed as insufficient. • JTL is a Datalog-like language for code queries that can (among other things) replace AspectJ's pointcut sublanguage. • Based on Query-by-Example. • No need for looping constructs. • Rich predefined predicate library. Overview Problem Solution More Results
JTL Examples • Search for float-typed static method: • Regular exp.: static.*float.*\(.*\) • Inaccurate. • AspectJ: static float *.*(..) • OK, for this specific use. • XQuery: /class/method[ .//returns/@type="float" and @modifiers="static" ] • Insane. • JTL: static float method Overview Problem Solution More Results
JTL Examples • Another query: "any public field in a class that contains no setter or getter methods". • Regular exp.: impossible. • AspectJ: impossible. • XQuery: Possible, but… • JTL: Overview Problem setter := publicvoid 'set[A-Z]?*'(_); getter := public !void 'get[A-Z]?*'(); field_in_plain_class := public field, declared_in[C], C.members: { no getter; no setter; }; -- Given the standard library: field_in_plain_class := public field, declared_in[C], C { no getter; no setter; }; Solution More Results
Factories • Remove the need for the Abstract Factory and Factory Method design patterns. • Used extensively in middleware frameworks (e.g., home objects in J2EE, Bean Factory in Spring, etc.) • Ensure all instances are created with shakeins applied – without touching client code. • Constructors only deal with initialization; factories deal with object creation. • Including: which actual class to instantiate, or if we should use an existing instance. Overview Problem Solution More Results
Factories • With existing patterns: if class S is changed to become a singleton, all existing code that calls new S() must be changed to call S.getInstance(). • With factories, the change is local to S. • The factory S() will manage a single instance. • With existing patterns: if B extends A, and A has controlled instantiation via a getInstance method, what will B.getInstance() return? • With factories: such static methods are not needed. Overview Problem Solution More Results
Object Evolution • Dynamic reclassification: allowing an object to change its type at runtime. • e.g., prince → frog. • Supported by Smalltalk, several others. • Many real-world uses • e.g., State design pattern. • Type safety problems… Prince p = new Prince(); if (…) p → Frog(); p.drawSword(); // potential runtime type error • Our solution: limit to monotonic changes only. • "Object evolution" -- moving down the inheritance tree. • Prince → king is okay! Overview Problem Solution More Results
Object Evolution Flavors • I-Evolution: Movement down the inheritance tree. • Might fail if runtime type differs from static type. • M-Evolution: Move by applying a mixin to the runtime type of the object. • No "type mismatch" possible. • Failure even less likely with idempotent mixins. • S-Evolution: Move by applying a shakein to the runtime type of the object. • Type not changed at all (only the class), so… • Changes can be undone – still monotonic! • Result: shakeins as "dynamic aspects". • I-Evolution: Movement down the inheritance tree. • Might fail if runtime type differs from static type. • M-Evolution: Move by applying a mixin to the runtime type of the object. • No "type mismatch" possible. • Failure even less likely with idempotent mixins. • S-Evolution: Move by applying a shakein to the runtime type of the object. • Type not changed at all (only the class), so… • Changes can be undone – still monotonic! • Result: shakeins as "dynamic aspects". Overview Problem Solution More Results