With the growth of the Internet and its world-wide use for business, leisure, or calculus, modern applications tend to be more and more distributed and shared by wide communities of users. Despite some standards begin to allow world-wide access of these applications (e.g. the Sun JSP), the world-wide deployment, maintenance, modification of these applications can be tricky and the programmers and architects are facing difficult issues.
First, for the same functional or business requirements, the fact that the application is distributed and used by a great deal of users makes some extra-concerns appear such as security, fault-tolerance, data-availability, or caching consistency.
Second, the design choices that are made during the development of distributed applications are more crucial than in centralized applications since design mistakes can lead to a total re-deployment of all the application's structure or concerns and make the project waste precious time.
Last but not least, most of the identified concerns inherently depend on the application final semantics and are tightly linked to the business concerns, making the modularization of distributed applications extremely difficult and leading to design dilemmas and hardly maintainable applications.
In the last few years, a new kind of software design solution is beginning to appear with the growing interest of the computer scientists community for the ideas coming from the Aspect-Oriented Programming (AOP) [4]. AOP is a neat and powerful way to modify the application semantics so that it supports several needed concerns. Neat because it ensures a very clean separation of all the application identified concerns. Powerful because it is supported at a programming level and thus does not require any extra design efforts to achieve the concerns mixins. We have been successfully experimenting these ideas on an Aspect-Oriented middleware called Java Aspect Components (JAC) [11] and several aspects have been implemented on it such as persistence, authentication, or distribution.
Lately, these programming-level ideas also tend to be applied at a design-level and many works based on UML [15] try to incorporate aspects within the application development cycle not only for the programming phase but also at the design phase [7,5]. We believe that the Aspect-Oriented Software Design (AOSD), is able to bring an homogeneous answer to the design issues that are hardly completely solved by most of the existing solutions. However, we also believe the AOSD alone is not sufficient to cope with all the issues brought by distribution. As a consequence, we couple the AO concepts with some extra abstractions that allow us to use the expressive power of AOSD for distributed applications. In this paper, we progressively describe a Distributed Aspect-Oriented Software Design (DAOSD) notation that comes from our concrete experience with JAC and show that it can be advantagely used to model and design complex distributed applications.
Firstly, section 2 focuses on the group notion, a key concept for DAOSD and shows how groups are tightly linked to aspects. Secondly, section 3 shows how aspects can be defined and used in a group-oriented world and what are the required concepts and their semantics. Finally, the last section gives a distributed application sample focusing on three aspects and showing how our notation allows the designer to define distributed applications with cleanly separated concerns.
As a background task, we have been working for several years on defining a Group-Oriented Paradigm (GOP) that allows the programmer to specify its applications not only with interoperating objects, but also with interoperating sets of objects [17]. Our motivation for this paradigm comes from the observation that, in an object-oriented distributed application, sets of distributed objects can often be related together by the fact that they interoperate to realize a functionality in a distributed fashion (e.g. a DNS lookup, a consistency protocol, ...). Thus, in exactly the same way the Object-Oriented Paradigm aggregates data and functions within a well-encapsulated entity, it seams very natural to group sets of distributed objects and distributed functionalities around a distributed - but well-encapsulated - entity called a group.
Despite some research projects within the distributed Operating Systems and Middleware community point out the need of a high-level language based on sets or groups of objects, for instance the Jgroup model [12]1, we think
In short, using groups as a design-level concept allows the designer to add a new point of view on the application model (which can be used as a refinement step) that defines distributed applications behaviors regardless the way they are actually distributed:
Introducing a new modeling entity may seem confusing. However, the Group-Oriented Paradigm (GOP) is very natural since it is completely coherent with the Object-Oriented Paradigm (OOP). Table 1 shows the correspondence between the different concepts of the OOP and the GOP.
| OOP | GOP | |
| Structuring unit | object | group |
| Functional unit | object service | group service |
| Implementation operation | function evaluation | (client-server) member-objects interaction |
| State | set of encapsulated values | set of member-objects |
| Typing implementation unit | class | group-class |
Figure 1 shows how groups abstract the distributed nature of some programs. The upper part of the figure describes a group-level specification of an abstract application that defines a viewer class that uses a document class (the used formalism is explained later in sections 2.3 and 2.4). Conceptually speaking, this viewer is able to invoke a search service on the document that returns all the locations of the given word within the given document.
Concretely speaking, the implementation of this application can be very centralized or very distributed. The middle and lower parts of the figure show two extreme concrete implementation samples:
Let us take the case-study (see figure 2) of a distributed application composed of some client front-ends using three objects (servers), each of them being instances of different classes (they provide a different subset of services). Imagine now that someone provides you a Cache class that you want to use to improve your program performances by caching some results of your objects methods.
There are currently four different means to make the application handle this new concern.
To work around this issue, we think that the distribution concern must be handled at the design-time and that the designer needs a suited modeling notation to express the fact that the application is distributed or not. Moreover, the notation should support separation of concerns so that an implementation change or evolution (such as adding a caching concerns) does not imply changes within the functional model of the application and does not interfere with the distribution concern. A first step to reach this goal is to abstract the distribution concern by using a group-oriented paradigm to design the application.
Regarding the GOP definition, it is quite straightforward to define groups in a high-level modeling language like UML. Indeed, since groups are the projection of the objects within an object-interaction based universe through an homomorphism, the same modeling language can be used for the groups. All we need is a way to distinguish the group-level elements from the regular ones. This can be easily done in UML by using a << group >> stereotype to mark the group-classes.
However, this is not sufficient since the designer should also be able to tell which objects are members or not of the group. This can be done by using a relation between the group and the members. The UML relation that is the closest from the membership semantics is the aggregation/composition (depending if members can be removed or added to a group during its life-time). Thus, we use a aggregation relation that is stereotyped with << membership >> to define what are the members of the groups.
One problem still remains when the group needs to exclude some instances of a given class regarding some criteria. Indeed, at a class-level, UML can only tell the number of instances that are related to the group but not which particular ones. We could solve this issue by using an object diagram but this solution is poorly supported by code generators and it is too static (the instances are chosen at design-time and cannot evolve). For this reasons, we choose to let the designer to tag the relation in order to filter the instances of the related class that must be members. This tagged value should be understood by the code generator and the generated code must be compliant with the target environment (for instance, the tagged value may be a regular expression on the name of the object as it is registered within the naming service if any).
A group offers to its client(s) a set of functional services. These are the sum of the accessible group-level services of all the members of the group. By group-level service, we mean that an object that is not member of this group can access the service through the group interface. As explained in section 2.1, what is interesting with group-level services is that their locations are transparent for the client (the client do not know within which member-object(s) the service is implemented). It is the group-level service implementation role to decide which member is used, depending on the current interaction.
Discovering groups and group-classes is done exactly the same way objects and classes are discovered during a regular modeling process. For instance, all the caches form a group that provides a group caching interface that can be used by some other groups to implement caching. Figure 4 shows the definition of the Cache group-class for our example. It is composed of three members, all instances of the Cache class and provides group-level services that dispatch client group requests to the member-objects.
In the caching sample explained in section 2.2, the semantics modification introduced by the caching concern into the application is quite symmetric. Concretely, it means that all the objects that are modified to implement caches (the server objects) can be seen as modified by the same abstract transformation rule. However, these objects are functionally heterogenous (in other words, they are not instances of the same type). Thus, another designation mechanism is needed to express the fact that a set of well-defined objects implements the same concern (here the caching one).
This need for a new kind of structured elements brings us to focus on the group notion. If we look at the group notion very carefully, we can notice that it is tightly linked to aspects. Indeed, contrary to a class that abstractly represents a set of instances realizing the same functional characteristics, a group is, in our definition, an abstract representation of a set of instances that do not necessary have homogeneous functional types but that are logically grouped together because they implement the same service (server-groups) or use the same one (client-groups).
Figure 5 naively 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 on 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.
Once the GOP is introduced as an entire part of the modeling language, the modeling of aspects becomes straightforward. In fact, if we take again the caching concern presented in sections 2.2 and 2.5, modeled through a GOP, it appears that the introduction of the caching concern within the original client-server application is abstractly done by the use of the services the Cache group interface provides to the servers group. This can be easily represented in UML by using the << use >> relation as represented in figure 6. In the general case, implementing a new concern may require the use of several group-level 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 the following.
The model of figure 6 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(). This relationship implementation requires the application to modify the client-group member-objects implementation to introduce the caching concern within the application. If this concern implementation is modularized (i.e. if the code that implements the caching concern introduction is locally defined), then the implementation technique follows the AOP guidelines and we can call the obtained module an aspect.
To express the fact that a use-provide relationship is implemented in an aspect-oriented fashion, the application designer must add a tagged value ''aspect:aspectName'' to all the << use >> relationships implemented by the aspect called aspectName (see figure 6).
Thus group-oriented modeling allows the designer to explicit in a comprehensive way what parts of the distributed application are aspects and what parts are not2. In fact, for each modeled group-level use-provide relationship, aspect-oriented techniques can be used to separate concerns within the final implementation.
We now show how we model the use-provide relationship in our next step of refinement. Accordingly to Aspect-Oriented guidelines, this modeling phase preserves the separation of concerns and do not modify the client-group definition. Therefore, to precisely define the relation semantics, we need to introduce new modeling concepts.
The main difference between aspect-oriented design and other classical design is that the first furnishes some concepts to allow the designer to externalize the dependencies that inherently exist when a client uses a set of services for its implementation.
We recommend to model related use-provide relations within separated diagrams. Since these diagrams use the aspect-oriented design features, we call it an aspect-diagram.
Aspect-diagrams central elements are aspect-classes. An aspect-class is like a regular class (with attributes and regular methods), except that it can contain methods that may have particular semantics. Globally speaking, the ideas of all these semantics is to enable a semantics extension on a given client-group implementation point so that it actually uses a server-group (such as a cache group). In other words, an aspect-class is the definition of an abstract protocol that throws the basis and generic rules on how to use a set of services in order to implement a concern.
Aspect-methods parameters have special semantics. Some optional Meta-Object Protocol [16] (MOP) arguments can be defined to dynamically access some information on the client-group interaction point within the aspect-method implantation. The MOP arguments depends on the target aspect system and can be of different nature depending on the employed implementation technique. In addition to MOP arguments, aspect-methods can also define and use special kind of arguments called contextual arguments. Contextual arguments do not need to be explicitly passed by the client but can be dynamically set by any object implementation. When a contextual argument is defined at a point of the execution flow, then it is valid for all the subsequent object interactions and passed to all the aspect-methods that need it.
The designer can explicit one of the aspect-method extending semantics by using one of the furnished stereotypes on the methods of the aspect-class. Here are the possible stereotypes and the meanings of the method typed with them.
On contrary, role method is an extension means that does not modify any execution point but adds some properties to existing objects (the modified point is here more structural). The role mechanism can be related to the specialization mechanism but in an aspect-oriented fashion.
Figure 7 shows the caching aspect-class (with the << aspect >> stereotype) of our example. It defines a whenWrite << after >> method that sets the value of the cache (using setValue) and a whenRead << around >> method that reads the value from the cache (using getValue) except if the contextual parameter forceInvalidate is defined. As any class, an aspect can handle an association towards other classes, here the Cache group class.
A pointcut relation allows the designer to link some aspect-methods to some points of the client-group. Depending on the target aspect-system, a pointcut can be of different nature such as a class, a set of objects, or a set of methods. In our methodology, the pointcut can be associated to a group to express the fact that the modification implemented by the aspect not only crosscuts the program locally, but also on a set of distributed objects that can be located on different physical hosts.
The pointcut relation is an oriented association from an aspect-class towards a class or a group. It particularizes how the aspect-class methods actually modify the base-class or group implementation (i.e. the client-side of the AO use-provide relation). The association is stereotyped with << pointcut >> and the roles have very special semantics since they are used to tell which methods of the client-group are extended and by which aspect-methods.
Here is the detailed semantics (refer to figure 8):
| Keyword | Points out |
| ALL | all the methods of I |
| CONSTRUCTORS | all the constructors instance methods of I |
| SETTERS | all the methods of I that are setters |
| GETTERS | all the methods of I that are getters |
| SETTER(attrname) | the setter of a given attribute |
| GETTER(attrname) | the getter of a given attribute |
| MODIFIERS | all the methods of I that modify some instances states |
| MODIFIERS({attrnames}) | all the methods of I that modify the instances fields contained in the {attrnames} list |
When a group uses another group to extend its functional or non-functional characteristics, (in our example, the server group becomes a cached server-group by using the cache group), the location where the server services (here the cache services) are implemented is not always clearly specified at a group-level. However, within distributed environments, these implementations locations may be crucial for the final willed semantics.
For instance, if we take the cache example, it is quite obvious that the resulting effects of the cache services implementation are not the same if they are located on the server sites or if they are located on the client sites. In the first case, caches allow the servers to avoid heavy calculus if several read accesses are performed from the clients (thus reducing the CPU usage). In the second case, the caches avoid network traffic by enabling local accesses on the read data. Both implementations are valid and can be used separately or together depending on the application.
Thus, when using our methodology to model aspect-oriented applications within distributed environments, the designer should be aware of this issue and locate the pointcuts at the client-side, or at the server-side.
If we look again at figure 9, we can say that it is a server-side caching policy since the members of the group are the a, b, and c servers (the pointcut is defined on the group Server).
If we now want to design a caching policy that implements some caches at client-side, we can process as depicted in figure 10. First we define the client and the server groups. The client-group uses the server-group through a << use >> relationship which means that it exists some group-level interaction between them. Then, we precise that the cache group is used by the client-group and not by the server one (upper part of the figure). The lower part of the figure shows the refinement of this model with a caching aspect. Note that the pointcut relationship ends to the Client group-class (and not to the server anymore) and that the pointcut denotes the client-side invocations (the ! symbol) of the Server services (and not the server-side execution (the ? symbol)).
Notice also that in our client-side cache implementation, we only use the whenRead role for applying the caching aspect. In fact, when the caches are located on the clients, we assume that they can not write any data to the servers since it would bring some consistency issues3. If some writing accesses are actually performed, then the application programmer must add a consistency aspect that defines the whenWrite role and, for instance, invalidate all the clients caches when such an event occurs.
We have been tested our modeling on several aspects. Most of them are fully implemented within the JAC [11] platform. Table 3 sums up the locations of their pointcuts.
| Aspect | Location | Task |
| authentication | client-pointcut | asks the user to login |
| server-pointcut | checks the validity of the user informations | |
| session | client-pointcut | links the user informations to the session id, saves them locally if needed |
| server-pointcut | none | |
| persistence | client-pointcut | none |
| server-pointcut | saves the data that is written within a storage | |
| GUI | client-pointcut | asks for the parameters of the invoked services if needed |
| server-pointcut | updates the objects views if their states change | |
| crypting (not implemented yet) | client-pointcut | encodes the data |
| server-pointcut | decodes the data |
Within a simple distributed application, we assume that some servers located on several sites implement a set of services for a set a clients that are located on other sites. The servers form a group Server with an interface that is known from the clients. At a group-level, we do not have to represent the way the group services are accessed and where they are implemented. Symmetrically, the clients that access the servers from a group Client that can be formed of an undefined number of members located on remote sites. All the actual distribution information of the group will be defined in further refinement steps. At this level, the only interesting information is that the client-group uses some services of the server-group. This is materialized by a << use >> relationship (see the lower part of figure 11).
Regarding the authentication concern, the choice is up to the designer. A quite classical choice is to define the authentication as follows:
Once the dependencies are made between the different identified groups, the designer must choose which of them may be implemented in an aspect-oriented fashion (as depicted in section 3.1). Figure 12 shows the aspect:authentication tagged relationships that pose the bases of the authentication aspect specification.
Once the dependency diagram is made, all the dependencies that handle the authentication concern can be defined as an aspect. Deciding which relationship is part of an aspect or not is domain dependent and it is the designer, helped by a domain expert that decides which set of relationships must be modularize in an aspect depending on their logical understanding and experiences.
Figure 13 shows a possible modularization of the authentication aspect that we consider to be relevant to our experience. Note that each pointcut defined by an aspect is most of the time the same as the number of << use >> found in the previous step.
The two << before >> methods of the aspects define behavior to add before a server service invocation or execution. Each one of the pointcut relationships corresponds to a method and thus defines to which member objects the aspect methods must be applied and the location of these applications. The diagram can then be read as follows.
The programming of the aspect-methods can be done in several general-purpose aspect-oriented languages such as AspectJ [3] or JAC [11].
The following code is the JAC version of the client-side and server-side aspect-methods.
// get the display reference from the context
// note that this contextual attribute must be set by the
// client program or by another aspect such as a GUI aspect
Display display = (Display)attr("display");
if (display==null) throw new InvalidDisplayException();
String[] authInfos = new String[2];
// open an input box to ask the infos (this is a
// thread-blocking call)
display.showInput(authInfos);
// put the user answer in the context
attrdef("user", authInfos[0]);
attrdef("password", authInfos[1]);
// proceed the invocation
return proceed();
}
public Object checkAuthInfos() {
// get the authentication information from the context
String user = (String)attr("user");
String password = (String)attr("password");
// ask the authentication server to authenticate this
// user (a false return value means that the access
// is not granted for this user)
if( ! authentication.authenticate(user, password) )
throw new AccessDeniedException();
return proceed();
}
In this section, we define two other useful aspects. Since the authentication aspect has been detailed and that we now want to focus on the composition issues, we just furnish the final UML model.
Within a distributed environment, having an authentication aspect alone is not sufficient to bring a fully trustable security. Indeed, the user and the password data are private and must not be compromized. Consequently, a privacy aspect should also be used so that the application is secure.
Figure 14 shows the fully modeled privacy aspect that uses a encryption algorithm based on public host certificates and private keys. It works in both directions, i.e. it encrypts/decrypts the parameters and the returned value when a remote service is invoked.
One detail still needs to be handled by the distributed applications when the clients use the remote services. Indeed, for the moment, the two over-depicted aspects are working on each interaction independently. This means, for instance, that the system asks the client to authenticate each time it accesses a remote service. Thus, the system should be able to handle a sequence of interactions (coming from the same client host) as part of the same client's session.
Figure 15 shows how we implement this feature. In fact, the sessions can be described as client-side persistence features. The two aspect methods are used to save the sessions related informations within a storage (that can be volatile in some cases). The session data are related to the current user context and can be various private informations depending on the other aspects that are woven in the system (for instance, it can be a user, a password, or a crypting private key). Once the session information are saved, they are restored before any other manipulations so that the other aspects do not have to fetch them anymore (by asking to a remote server or by interactively requesting the user to fill the missing data through an input/output device).
The use of before, after, or around aspect methods implies that the aspects are potentially able to compose well together since all the extensions that are provided by the aspects consists in adding some extra behavior to the existing programs. The aspect-oriented technology provides the mechanisms and the support to compose the extensions in a controlled and secure way. However, these composition mechanisms may depend on the target system. Thus, the best way to configure the aspect composition is to allow the user to add a set of composition rules on the classes of the base application (the one that support the aspects). These composition rules are tagged values that are platform dependent.
If the target platform is JAC (or AspectJ), then the composition rules can be expressed as a list of precedence rules between all the aspect methods. By expressing these precedence rules, the designer avoids conflicting semantics that could arise when aspects are composed together. By the time, there are no other means than the natural language to really explain why an aspect method must be called before or after another one since it sometimes depends on the appreciation and experience of the designer4.
This paper proposes a UML notation to design applications in an Aspect-Oriented fashion that is also valid for distributed applications. Our effort is bottom-up and all the presented concepts are extracted from a practical programming approach and are thus concretely linked to the actual problems encountered when achieving separation of concerns. However, we also tried to be as independent as possible from low-level paradigm and we believe that this notation can be successfully applied to several target AO platforms or languages such as AspectJ and even to regular ones. In this last case the transformations achieved to map the UML design towards the concrete implementation are more complex.
In this paper, we lack some place to explain how distributed applications are refined. However, in [1] we present an Aspect-Oriented refinement model that allow the designer to deploy and distribute its application in an Aspect-Oriented fashion. With this architecture implemented in JAC, Aspect-Oriented applications defined in our notation can be refined towards an actually running distributed application.
In the future, this notation should be supported by UML tools such as Rational Rose or ArgoUML, so that advanced separation of concerns is support for all the steps of the software development. To us, this full support should greatly enhance the software specifications and implementations quality.
1Jgroup proposes a very interesting programming platform over Java where the programmer can use and manipulate groups of distributed replicas to construct reliable applications
2Note that this model is also valid for regular classes. Groups are here used as an abstraction to get rid of the distribution issues at this specification step.
3This kind of implementation is typically used by Internet navigators such as IExplorer or Netscape.
4However, some automatic rules can be valid if we restrain the possible
semantics of the aspect-method. A first explanation on these rules
have been presented in [2] and is still being investigated
in more detailed research.