Composing Concerns with a Framework Approach Constantinos A. Constantinides1,2 and Tzilla Elrad2 1
Mathematical and Computer Sciences Department Loyola University Chicago
[email protected] 2
Concurrent Programming Research Group Computer Science Department Illinois Institute of Technology, Chicago elrad@ iit.edu
Abstract As concurrent software systems become larger, the interaction of their components is becoming more complex. This interaction may limit reuse, making it difficult to validate design and correctness and perhaps forcing reengineering of these systems in order to meet future requirements. In order to reduce this complexity and to build stable and adaptable concurrent software systems, we present an approach that emphasizes the separation of the interaction components from the functional components. This approach targets the design and development of open concurrent software systems where the functional requirements, concurrency requirements, and structural requirements can be easily designed, implemented, and reused.
1. Introduction As the size of software systems increases, their design has reached a complexity that requires software engineers to revisit the principle of separation of concerns, which refers to the realization of system concepts as separate software units. This principle is essential to software development since its benefits include better analysis and understanding of systems, easy adaptability, maintainability and high degree of reusability. At the same time, separation of concerns can only be beneficial if the different concerns can be effectively composed to produce the overall system. In order to support separation of concerns the OOP paradigm seems to work well only if a problem can be described with relatively simple interfaces among objects. Unfortunately, this is not the case when we move from sequential programming to concurrent and distributed programming, as the component interaction violates simple object interfaces. As a result, the benefits associated with OOP no longer hold. One of the reasons is the inherent structure of
today’s software systems that conceptually does not lead itself to be safely decomposed. Reengineering of these systems is needed in order to meet future changes in requirements. This component interaction is based on a number of properties such as synchronization, scheduling, and fault tolerance. These are properties that affect the performance and semantics of the system, and the term “codetangling” (Kiczales et al., 1997) is referred to as the phenomenon where the implementations of such properties (called aspects) cut across groups of functional components. This code-tangling and the resulting high coupling of components destroys modularity, making the source code difficult to develop and difficult to understand. It also limits reuse, making the source code difficult to evolve. It further makes programs more error prone. In essence, it destroys the quality of the software. In (Bergmans and Aksit, 2000) the authors refer to these phenomena as “composition anomalies”. This composition anomaly requires a shift in the methodologies used to separate concerns. There are currently a number of different proposals that address advanced separation of concerns. Although the mechanism of weaving lies at the center of their differences, they all have the same goals: Better abstraction than what has so far been achieved with current methodologies, higher modularity and higher degree of reuse. Some of the open issues regarding the design and implementation of architectures lie at the level at which aspects and components integrate. Should the integration be at the source code or maybe at the object code? How do we better express aspectual properties of systems? Should we use an aspect language or a framework approach? To what degree should an aspect oriented architecture support an open system? Should it enable dynamic adaptability and expandability? Should it support reusability? Should it further enable formal verification of system properties? In
this paper we present the architecture of a framework that supports separation of concerns and addresses these issues.
interaction between components and aspects. The subsequent subsections will discuss the analysis and design phases of the framework.
2. Requirements for Concurrent Systems Proxy
Aspect Moderator
In (Constantinides and Elrad, 2000c) we discussed a number of desired requirements for concurrent systems in order to provide an advanced separation of concerns. Ecommerce and online client-server applications, like trouble-ticketing systems, on-line reservation systems, timecard reporting systems, and online auctions, are becoming increasingly popular. To ensure high availability and reliability, these systems are often designed according to the design methodology of open software systems. Generally, the functional components of a concurrent system are stable. On the other hand, the interaction components are volatile and reactive to the environment needs. During the analysis phase of these systems, it is important to identify and isolate the functional components from the interaction components. Interaction components are used to address requirements like load balancing, fault tolerance, throughput, security, audits, location transparency, concurrency, and coordination. Object-oriented techniques have indeed supported reuse of these components to the degree that it satisfied software developers but failed to satisfy the software system architects. Software architects are very much interested in the common collaboration for a cluster of components. Object-oriented design techniques have showed their effectiveness to capture functional requirements into components. There is, however, little evidence that OO design techniques have fully resolved the software reusability problem. Recent research in concurrent objectoriented programming has showed that mixing the concurrency code along with the functional code may impede reuse and make it very difficult to extend or reuse the functional or concurrency components in isolation from each other. The structural properties (ilities), of the software systems are usually scattered across the functional components. In OOP, objects are the basic units of computation and the software system is composed of a set of objects and a set of interaction scenarios. These objects may play the role of a servant object, a client object, or perhaps both roles. These objects may reside on the same host or distributed across the network.
Functional Component
EvaluateAspect ASPECT FACTORY
RegisterAspect
SERVICE A SERVICE B
ASPECT BANK
CreateAspect
Security Security Synchronization Synchronization Scheduling Scheduling
Figure 1. Architecture of the Aspect Moderator Framework
4. Analysis Phase This section will discuss the analysis phase of the framework. As an example, we will use the trouble-ticketing system. This is an application where clients open (place) tickets on a server, and assign (retrieve) tickets from a server. This application is based on the producer consumer protocol with the use of a bounded buffer. The framework can be studied in two different stages: initialization, and method invocation. 4.1 Initialization Phase During initialization, the proxy to the functional component (TicketServer) will request from a special object (AspectFactory) the creation of two objects that capture the synchronization constraints that are associated with services (methods) open() and assign() (Figure 2). Upon creation, objects OpenSynchronizationAspect and AssignSynchronizationAspect are registered (stored) in the AspectModerator object that will need to reference them during method invocation in order to coordinate their interaction with the functional components.
3. The Software Architecture of an AspectOriented Framework
4.2 Method Invocation The proxy to the functional component is responsible to evaluate each of aspects that are associated with each one of the services that are defined on the functional component. The result of this evaluation may cause the service to
In the Aspect Moderator framework (Figure 1), a concurrent object is represented as a cluster of co-operating classes that handle the creation of aspects as well as the 2
be invoked, cause the caller to wait, or abort the activation. Before executing each on the functional component, the proxy object calls the moderator object to evaluate the aspect code that is associated with that method (Figure 3). Methods in the functional component that are associated with aspect objects are referred to as participating methods. Every participating method is guarded by preactivation and post-activation phase.
Method pattern is to define an interface for creating an aspect object, but let the requestor decide which class to instantiate. ACTOR
:Aspect Moderator
:TicketProxy
:OpenSync
:Ticket
Aspect
put (ticket) preActivation (open, sync)
:TicketProxy
:Aspect Moderator
:AspectFactory
precondition()
createAspect (Open, Sync) new
[precondition == true] open (ticket) :OpenSyncAspect
postActivation (open, sync)
registerAspect ()
postaction() createAspect (Assign, Sync) new
:AssignSync Aspect
Figure 3. Method Invocation Phase
registerAspect ()
Figure 4 shows a class diagram with the roles that classes and interfaces play in the Factory Method pattern. Using the organization shown, a TicketServerProxy object calls method createAspect() of an object that implements the AspectfactoryIF interface by passing some parameters that tell that method which class that implements the AspectIF interface to instantiate.
Figure 2. Initialization Phase Upon a message reception that involves a participating method, the proxy will delegate the responsibility to the aspect moderator to evaluate the preactivation phase. The moderator will, in turn, evaluate all required aspects of the participating method by calling the precondition of all required aspects. Upon successful return of the preactivation phase, the proxy will call the actual participating method. Once execution is complete, the proxy will initiate the postactivation phase, by delegating responsibility to the moderator to evaluate the postactivation on the given method. During this phase, the aspect moderator will call the postaction of the required aspects.
TicketServerProxy 1 requestor
Requests-creation
creator AspectIF Uses
precondition():int postaction():void *
*
AspectFactoryIF createAspect (String methodID, String aspect, TicketServerProxy component):object
5. Design Phase ConcreteAspect
This section will describe the design phase of the framework.
AspectFactory precondition():int postaction():void
5.1 Initialization: Deploying the Factory Method Pattern
Creates 1 createAspect (String methodID, String aspect, TicketServerProxy component):object
Figure 4. Creating Aspects Using the Factory Method
The Factory Method pattern (Gamma et al., 1995) can be used to create the required aspects for the participating methods of the functionality class. All aspect objects implement the AspectIF interface. The intent of the Factory 3
The implementation of the OpenSynchronizationAspect is defined by precondition() and postaction(). Method precondition() is called during the pre-activation part of method invocation. During precondition() the constraints dictate that if the shared object (TicketServer) is not full, then the method returns true. The postaction() method will be called during the post-activation part of method invocation phase and it will ensure the incrementing of the counters of the shared resource.
5.1.1 Participants of the Factory Method Pattern The participants of the Factory Method pattern are as follows: • TicketServerProxy: It acts as a creation requestor for aspect objects. • AspectFactoryIF: This is an application-independent interface. It declares the Factory Method, which returns an object of type AspectIF by taking whatever arguments are needed to deduce the class to instantiate. • AspectFactory: This is an application-specific class. It implements the Factory Method to return an instance of ConreteAspect. • AspectIF: defines the interface of objects the Factory Method creates. • ConcreteAspect: This is a concrete class instantiated by the objects that participate in the Factory Method pattern. It implements the AspectIF interface.
public class OpenSynchronizationAspect implements AspectIF { public TicketServerProxy component; protected int ActiveOpen = 0; … public int precondition() { if ((component.noitems < component.capacity) && (ActiveOpen == 0)) { ++ActiveAssign; ++component.noitems; // retutrn resume } else { // return blocked} } public void postaction() { --ActiveOpen; component.assignPtr = (component.assignPtr + 1) % component.capacity; }}
5.1.2 Code Example: Initialization Phase The constructor of TicketServerProxy contains the code to request 1) the creation of the two aspect objects, and 2) their registration with the aspect moderator object (Figure 5). The call from the TicketServerProxy to the AspectFactory passes a number of arguments that define the type of aspect instantiation (Figure 6).
Figure 7. OpenSynchronizationAspect Class As aspect objects are first class abstractions (values), they can be stored in an array within the aspect moderator object. Further, they can be referenced (accessed) from the array. In examining the usage and importance of aspect registration, we introduce the concept of an aspect bank, which provides a hierarchical two-dimensional composition of the system in terms of aspects and components. Upon creation of the aspect objects, the proxy will call the aspect moderator object to store (register) these aspect objects for reference during the next (method invocation) phase. The call to create() method will return a reference to an instance of an aspect class. This reference is parameterized to method registerAspect() of the moderator object. Method registerAspect() will simply create an entry in a two dimensional array within the moderator object to be used for future reference (Figure 9).
TicketServerProxy (AspectModerator moderator, AspectFactory factory) { … moderator.registerAspect(OPEN, SYNC, factory.create(OPEN, SYNC, this)); moderator.registerAspect(ASSIGN, SYNC, factory.create(ASSIGN, SYNC, this)); }
Figure 5. Constructor of TicketServerProxy Class public class AspectFactory implements AspectFactoryIF { … public Object create (String methodID, String aspect, TicketServerProxy component) { if (methodID.equals("Open")) if (aspect.equals("Sync")) return new OpenSynchronizationAspect(component); if (methodID.equals("Assign")) if (aspect.equals("Sync")) return new AssignSynchronizationAspect(component); return null;}
5.1.3 Method Invocation Phase Figure 10 shows the guarding of a participating method between methods preActivation() and postActivation() that are defined by the AspectModerator class (Figure 11).
Figure 6. AspectFactory Class
4
public void registerAspect (String methodID, String aspect, Object aspectObject) { if (methodID.equals("Open") && aspect.equals("Sync")) { aspectArray[open][sync] = aspectObject; } // similarly for assign() }
Method preActivation() will call precondition() in the method’s corresponding synchronization aspect. During precondition(), guards will validate the synchronization constraints of the invoked method, returning RESUME upon success. After successful return, the AspectModerator will return a RESUME to the FunctionalityProxy that will activate the method in the sequential object. The completion of the method execution will initiate a call by the TicketServerProxy to the AspectModerator’s postActivation phase. During postActivation(), there is a call to the postaction() of the method's synhronization aspect during which synchronization variables are updated. The proxymoderator object pair coordinate functional and aspectual behavior by handling their interdependencies. The UML class diagram of the aspect moderator framework is shown in Figure 12.
Figure 9. Registration of Aspect Objects public void open (Object the_value) { if (moderator.preActivation(OPEN) == RESUME) { super.open(the_value); moderator.postActivation(OPEN); } else // error } // similarly for assign() }
Figure 10. Guarded Methods in TicketServerProxy
AspectModeratorIF
Component
preActivation(String methodID):int postActivation(String methodID):void registerAspect (String methodID, String aspect, Object aspectObject):void
public int preActivation(String methodID) { int result = ERROR; if (methodID.equals("Put")) { synchronized(PutWaitingQueue) { synchronized(this) { result = ((PutSynchronizationAspect) aspectArray[put][sync]).precondition(); } while (result == BLOCKED ) { try { PutWaitingQueue.wait (); synchronized(this) { result = ((PutSynchronizationAspect) aspectArray[put][sync]).precondition(); } } catch (Exception exception) { return AspectModerator.ABORT; } } //while } synchronized return result; } // put //similarly for assign return result; } public void postActivation(String methodID) { if (methodID.equals("Open")) { synchronized(AssignWaitingQueue) { synchronized(this) { ((OpenSynchronizationAspect) aspectArray[open][sync]).postcondition(); } AssignWaitingQueue.notify(); } // synchronized queue } // open // similarly for assign }
Uses ComponentProxy 1 AspectModerator creator
requestor Requests-creation
creator AspectIF
*
int preActivation(String methodID):int postActivation(String methodID):void registerAspect (String methodID, String aspect, Object aspectObject):void AspectFactoryIF * AspectFactoryIF
precondition():int postcondition():void
createAspect (String methodID, String aspect, Application component):object *
Uses AspectFactory ConcreteAspect
Creates
1
createAspect (String methodID, String aspect, Application component):object
precondition():int postcondition():void
Uses
Figure 12. Class Diagram of the Aspect Moderator 5.3 Provision of Adaptability The key to a successful software architecture is to ensure that the software system is adaptable enough to meet future requirements. Let a new requirement state that authentication should be introduced to the system. The general architecture of the aspect moderator ensures adaptability both in terms of components and aspects. In the trouble ticketing system, an ExtendedTicketServerProxy, will request the creation of two authentication aspects from an extended factory component (Figure 13).
Figure 11. Illustration of Pre- and Post-activation
5
public class ExtendedTicketProxy extends TicketProxy { .. static ExtendedAspectModerator moderator; static ExtendedAspectfactory factory; ExtendedTicketServerProxy (ExtendedAspectModerator m, ExtendedAspectFactory af) { ... moderator.registerAspect(OPEN, AUTHENTICATE, factory.create(OPEN, AUTHENTICATE, this)); moderator.registerAspect(ASSIGN, AUTHENTICATE, factory.create(ASSIGN, AUTHENTICATE, this)); }...}
separation concerns, as it manages to retain separation in all stages of the development. From analysis we emphasize the decoupling between object concurrency constraints and object functionality. Functional and non-functional requirements can be easily designed, implemented, tested and reused. The Aspect Moderator framework complements the object-oriented and component-oriented technology in order to promote code reuse and to support adaptability of concurrent software systems. public class ExtendedAspectFactory extends AspectFactory { public static final String AUTHENTICATE= "Authenticate"; public ExtendedAspectFactory () {} public Object create (String methodID, String aspect, ExtendedTicketServerProxy component) { if (methodID.equals("Open")) if (aspect.equals("Authenticate")) return new OpenAuthenticationAspect(component); if (methodID.equals("Assign")) if (aspect.equals("Authenticate")) return new AssignAuthenticationAspect(component); return null; }}
Figure 13. ExtendedTicketServerProxy As a consequence of the addition of a new aspect, a request to a participating method will now have to be guarded by preactivation of authentication (done by the extended aspect moderator) followed by preactivation of synchronization. Only when both are true, then execution may proceed. The execution of the actual method is followed by the postactivation of synchronization followed by postactivation of authentication (Figure 14). public void open (Object the_value) { if (moderator.preActivation(OPEN) == RESUME) { super.open(the_value); moderator.postActivation(OPEN); } else {System.out.println ("ABORT");} } public Object assign() { Object the_return_value = null; if (moderator.preActivation(ASSIGN) == RESUME) { the_return_value = super.assign(); moderator.postActivation(ASSIGN); } else {System.out.println ("ABORT");} return the_return_value; }
Figure 15. Implementation of ExtendedAspectFactory public void registerAspect (String methodID, String aspect, Object aspectObject) { if (methodID.equals("Open") && aspect.equals("Authenticate")) { newAspectArray[open][authenticate] = aspectObject; } if (methodID.equals("Assign") && aspect.equals("Authenticate")) { newAspectArray[assign][authenticate] = aspectObject; }}
Figure 14. ExtendedTicketServerProxy
Figure 16. Registration of Authentication Aspects
The implementation of the ExtendedAspectFactory is shown in Figure 15. Figure 16 illustrates the registration of the two authentication aspect objects in the extendedAspectModerator object. Figures 17, and 18 illustrates methods preActivation() and postActivation() respectively in the ExtendedAspectModerator that handle the authentication aspect.
6. Conclusion In this paper we presented an approach for the design and development for open concurrent software systems. The Aspect Moderator framework supports a high level of 6
public int preActivation(String methodID) { int result = this.ERROR; if (methodID.equals("Open")) { synchronized(OpenAuthenticationQueue) { synchronized(this) { result = ((OpenAuthenticationAspect) newAspectArray[open][authenticate]).precondition(); } while (result == BLOCKED) { try { OpenAuthenticationQueue.wait(); Synchronized(this) { result = ((OpenAuthenticationAspect) newAspectArray[open][authenticate]).precondition(); … } // similarly for assign()
ming, volume 791 of Lecture Notes in Computer Science, pages 152--184. ECOOP, Springer-Verlag. Bader, A., Constantinides, C. A., Elrad, T., Fuller, T., Netinant, P., “Building Reusable Concurrent Software Systems”, Proceedings of PDPTA'2000. Bergmans, L., and Aksit, M,. “Composing Software from Multiple Concerns: A Model and Composition Anomalies”, Proceedings of ICSE 2000 2nd Workshop on Multidimensional Separation of Concerns. Constantinides, C. A., Bader, A., and Elrad, T., “A TwoDimensional Composition Framework to Support Software Adaptability and Reuse”, ed. Frakes, W. B., Software Reuse: Advances in Software Reusability; Proceedings of ICSR6, Springer-Verlag Lecture Notes in Computer Science.
Figure 17. Pre-activation in ExtendedAspectModerator public void postActivation(String methodID) { if (methodID.equals("Open")) { synchronized(AssignAuthenticationQueue) { synchronized(this) { ((OpenAuthenticationAspect) newAspectArray[open][authenticate]).postaction(); } AssignAuthenticationQueue.notify(); } } // open if (methodID.equals("Assign")) { synchronized(OpenAuthenticationQueue) { synchronized(this) { ((AssignAuthenticationAspect) newAspectArray[assign][authenticate]).postaction(); } OpenAuthenticationQueue.notify(); } } // assign }
Constantinides, C. A., Bader, A., Elrad, T. H., Netinant, P., and Fayad, M., “Designing an Aspect-Oriented Framework in an Object-Oriented Environment”, ed. Fayad, M. E., ACM Computing Surveys Symposium on Application Frameworks, Vol. 32, No. 1, pp. 41-, March 2000. Constantinides, C. A., and Elrad, T., “On the Requirements for Concurrent Software Ar-chitectures to Support Advanced Separation of Concerns”, Proceedings of OOPSLA 2000 Workshop on Advanced Separation of Concerns in Object-Oriented Systems. Fayad, M. and Cline, M. Aspects of Software Adaptability, Communications of the ACM, 39(10), 1996, 58-59. Gamma, E., Helm, R., Johnson, R. and Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, Reading, MA,1995.
Figure 18. Post-activation ExtendedAspectModerator
REFERENCES Gregor Kiczales, John Lamping, Anurag Mendhekar, Chris Maeda, Cristina Lopes, Jean-Marc Loingtier, and John Irwin. Aspect-Oriented Programming. In Proceedings of ECOOP ’97. LNCS 1241. Springer-Verlag, pp. 220-242. 1997.
Aksit, M., Wakita, K., Bosch, J., Bergmans, L., and Yonezawa, A. (1993). Abstracting object interactions using composition filters. In Guerraoui, R., Nierstrasz, O., and Riveill, M., editors, Object-Based Distributed Program-
7