16.2. DESIGN NOTATIONThis section describes our UML profile to support the design of aspects with JAC. Stereotypes are proposed to qualify classes implementing a non-functional concern (Section 16.2.1) and to qualify pointcut relations (Section 16.2.2). An example using these two concepts is given in Section 16.2.3. Section 16.2.4 discusses the similarities between AOP and the use-provide relationship. 16.2.1. Aspect Component ClassesAspect components (ACs) are the central point of our AO framework. They are the implementation units that define extra characteristics that crosscut a set of base objects. They are defined in classes called AC classes. The key characteristic of JAC is that the base objects that are involved in a crosscut are not necessarily located in a single container. An AC class is tagged with the <<aspect>> stereotype. It contains attributes and methods whose semantics differ from regular methods. AC methods are meant to extend the semantics of regular classes. The extension is performed on well-defined implementation points so that these points actually use aspect services to integrate new concerns. For example, a base class can be made to use a cache interface if the aspect implements some caching concern. Each AC method defines some code and extends the semantics of some base methods according to a modality defined by a stereotype. The stereotypes for an AC method m are:
For example, Figure 16-1 shows the caching AC class Caching (with the <<aspect>> stereotype). As its name suggests, this AC class provides a caching extension mechanism. The job of storing and retrieving values from the cache is delegated to the Cache (regular) class. The whenWrite method of the AC class Caching is tagged with the <<after>> stereotype. It is executed after any base method associated with whenWrite in the pointcut definition (see Section 16.2.2). The whenRead method is tagged with <<around>>. It is executed before and after some base methods. Figure 16-1. Definition of a caching concern with an AC class.16.2.2. Pointcut DefinitionA pointcut relation links an AC method belonging to an AC class to a set of elements of a base program. Pointcuts in JAC are limited to method calls; we do not introspect at the instruction level. Several arguments justify this decision. First, experiments with the fully reflective compiler OpenJava [32] taught us that reifying the whole code structure produces an unacceptable performance for real applications. Second, before extending the semantics of an application, it is necessary to understand its original semantics (we cannot extend something that is not clearly stated). Most of the time the original semantics are defined through an API, that is, primarily through method signatures. So base methods are definitively the best place to perform semantic extensions. JAC supports pointcuts on both classes as on individual instances. 16.2.2.1 Class-Level PointcutThis level is similar to the one found in AspectJ. All instances of the classes involved in the pointcut are extended by the aspect component. In this case, a pointcut relation is an oriented association from an AC class towards one or several classes. The association is stereotyped with <<pointcut>>. The roles have special semantics; they mention which methods of the client class are extended by which AC methods. The semantics of the elements mentioned in Figure 16-2 appear in the following list.
Figure 16-2. The pointcut association: relating aspects to classes.Figure 16-3 shows two pointcut relations that implement a caching aspect by using the AC class defined in Figure 16-1. This aspect diagram must be read as follows:
Figure 16-3. The full caching aspect.16.2.2.2 Instance-Level PointcutBesides the previously described mechanism, JAC also allows developers to define pointcuts on a per-instance basis. This can be useful in several cases. For instance, a persistence framework may need to define objects as persistent roots from which other referenced objects persist. A multi-user application may want to exchange data between several users by using specific objects. In a distributed environment, server objects may need to be customized, because they must cope with heterogeneous running contexts (in terms of network properties, provided and expected quality of service, and so forth.). In all these cases, instances belonging to the same class need to be extended differently. In Java, classes are straightforwardly named. Objects, on the other hand, present a problem: There is no direct naming scheme for identifying individual objects in Java. The solution taken in JAC is to let the framework attach a unique name to each created instance: The name is the concatenation of the class name in lowercase and of an auto-incremented integer (e.g., server0 designates the first created instance of class Server). The framework provides an API a way to retrieve objects based on their name and to customize names assigned to objects. For instance, a field known to be a primary key may be used to name objects. To let designers express a per-instance pointcut, aspect component side roles in the UML diagram (i.e., r1 in Figure 16-2) can be extended with an instance name or a regular expression on instance names. For instance, ?SETTERS|server0 designates the execution points of the setter methods of instance server0, and ?GETTERS|server[1-3] designates the execution points of the getter methods of instances server1, server2, and server3. When distribution comes into play, pointcut definitions can also be filtered based on container names (a container name being an RMI or CORBA URL depending on the chosen communication protocol between JAC remote containers). The idea is to let designers express behaviors that are dependent on the context in which components are deployed. For instance, one may want to install an authentication aspect only on specific critical hosts, whereas the rest of the application deployed on other hosts stays unmodified, or one may need to install a logging aspect only on a given container. To allow this, pointcut expressions can be extended with container names or regular expressions on container names. Merged with the previous extension for instance names, this leads to a complete scheme where pointcut expressions are of the form: qualifier methodExpression | instanceExpression | containerExpression with instanceExpression and containerExpression being optional. For instance, ?ACCESSORS||rmi://myHost/s1 designates the accessors execution points of instances located on JAC container rmi://myHost/s1. 16.2.3. A First Simple ExampleThis section illustrates the programming model of JAC based on the Caching aspect of Figure 16-3. The details of the API and some tutorials can be found on the JAC web site [10]. Listing 16-1 gives the code of the Caching aspect component. An aspect component extends the jac.core.AspectComponent class. Among other things, this class provides a method for expressing a pointcut. The parameters of this method are the base class this pointcut designates, the qualifier methodExpression as a string, the class containing the AC method, and the AC method involved in the pointcut. Listing 16-1 defines two such pointcuts. Additional pointcut methods are available when instanceExpression and containerExpression are to be associated with the pointcut. Listing 16-1. A simple aspect component implementing a caching concernimport jac.core.AspectComponent; import jac.core.Wrapper; import jac.core.Interaction; public class Caching extends AspectComponent { public Caching() { pointcut( "Server","?SETTERS",CachingWrapper.class,"whenWrite"); pointcut( "Server","?GETTERS",CachingWrapper.class,"whenRead"); } public class CachingWrapper extends Wrapper { private Cache cache = new Cache(); public void whenWrite( Interaction i ) { proceed(); Object value = i.arg[0]; cache.setValue(value); } public Object whenRead( Interaction i ) { Object value = cache.getValue(); if ( value == null ) value = proceed(); cache.setValue(value); return value; } } } AC methods are defined in wrapper classes (that extend the jac.core.Wrapper class). AC methods accept only a single parameter, a jac.core.Interaction instance. They may return any parameters. The rationale behind this constraint is that AC methods are upcalled by the JAC framework whenever a call to their base method is issued or executed (i.e., whenever the call matches the pointcut expression). An interaction object i provides data about the current call: arguments of the call (in the i.arg array), a reference to the base object (i.wrappee), and some methods to store and retrieve context parameters; for instance, parameters that can be added by an AC method on the caller side and that can later on be retrieved on the receiver side by another AC method. 16.2.4. Extended Design Notation for Distribution16.2.4.1 The Group ParadigmIn the caching example, the semantic modification introduced by the caching concern into the application is quite symmetric. Concretely, all objects modified to implement caches (the Server objects) can be seen as modified by the same abstract transformation rule. However, one may want to weave the caching aspect to different classes. Thus, another designation mechanism is needed to express the fact that a set of well-defined objects implements the same concern. This need for a new kind of structured elements brings us to focus on the group paradigm. A group is an abstract representation of a set of instances that do not necessary have homogeneous functional types, but are logically grouped together, because they implement the same service (server groups) or use the same one (client groups). Elements of a group "share the same common secret." Figure 16-4 represents the application of the caching aspect on a group of servers that implements the server part of a simple client/server application. We use an instance diagram so that it becomes obvious that the group on the top of the figure is a non-uniform set of instances (the three instances a, b, and c belong to three different classes A, B, and C). As shown in this figure, the application of the caching aspect creates a new group that contains instances of a Cache class that provides the caching functionality. In other words, we can say that these Cache instances belong to a server group that provides a caching functionality for the client group formed by the a, b, and c servers. Figure 16-4. Relating aspects to groups.16.2.4.2 A Group-Based Definition of AspectsAbstractly, the introduction of the caching concern within the original client/server application is done by the use of the services the Cache group interface provides to the servers group. This is represented in UML by using the <<use>> relation, as shown in Figure 16-5. In the general case, implementing a new concern may require using several interfaces. In these cases, several clients can be related to several servers through some <<use>> relations. Finally, a simple but sufficient definition of an aspect within this context is: Figure 16-5. The use relationship between a client group (the base program) and a server group (the aspect program).Definition 1 An aspect is the implementation of one or many use-provide relationship(s) between one or many client group(s) and one or many server groups. The model of Figure 16-5 clearly brings up a use-provide relationship between a client group (the servers), and a server group (the caches) that defines the group-level services getValue(), setValue(Object), and invalidate(). The implementation of this relationship requires modifying the client group member object's implementation to introduce the caching concern. At the analysis level, the application designer can add a tagged value aspect: aspectName to all the <<use>> relationships implemented by the aspect called aspectName to express that a use-provide relationship is implemented in an aspect-oriented fashion (see Figure 16-5). Thus group-oriented modeling allows the designer to specify in a comprehensive way which parts of the application are aspects and which parts are not. In fact, for each modeled group-level use-provide relationship, aspect-oriented techniques can be used to separate concerns within the final implementation. Note that this modeling, which is a high-level view of the application, defers the specification of pointcuts to some later stage refinements using the notation presented in Sections 16.2.1 and 16.2.2. Finally, each time the designer encounters the pattern of one or several use-provide relationships between groups, he can ask himself if an aspect would be well suited in this case. Whether to apply an aspect is mainly related to designer experience and choices. However, we can give some clues on when an aspect is better suited than a classical design. Aspects are often better when
Figure 16-6 sums up the notions introduced in this section and proposes a UML metamodel where additions introduced by JAC are drawn with bold lines. Figure 16-6. The UML extension metamodel. |