Chapter 2. Attacking the Problem with Aspects

We established in the previous chapter that existing techniques provide inadequate separation of crosscutting concerns. Aspect orientation solves this problem by providing a composition mechanism to compose additional behaviors from outside a class into the class itself. Composition can occur during compilation or runtime. But aspect orientation is more than just a composition mechanism. It is a way for you to separate the implementation of different concerns into separate modules. Through simple code examples in AspectJ—one of the most popular aspect technologies today—we demonstrate how aspects work and how aspect orientation helps you solve the problem of crosscutting and achieve better modularity.

Approaching a Solution with Aspects

Aspect orientation is a set of technologies aimed at providing better separation of crosscutting concerns. It is sometimes also known as advanced separation of concerns. Research in aspect orientation has been going on for a relatively long time, but it started gaining a lot of recognition from mainstream observers in 1997 when Gregor Kiczales from Xerox Parc presented his keynote presentation on aspect-oriented programming (AOP) at OOPSLA ’97. AOP, and specifically, AspectJ, is the most popular aspect technology [Kiczales et al. 1997] [Kiczales et al. 2000]. AspectJ is an extension to the Java language with new constructs to separate and modularize concerns:

  • Intertype declarations allow you to compose new features (attributes, operations, and relationships) into existing classes.

  • Advices provide the means to extend existing operations at extension points designated by pointcuts in AOP.

  • Aspects are a kind of building block used to organize intertype declarations and advices. Aspects can generalize other aspects.

AOP is not the only technique that deals with crosscutting concerns. A number of works from the research community, such as the following, address the issue:

  • Composition filters (CF) originated from the TRESE group at the Department of Computer Science of the University of Twente, Netherlands [Aksit et al. 1998] [Bergmans et al. 2001].

  • Multidimensional separation of concerns (MDSOC) originated from IBM Research [Tarr et al. 1999] [Ossher et al. 2000].

  • Adaptive programming (AP) originated from Center of Software Sciences, Northeastern University (NEU), Boston, Massachusetts [Lieberherr et al. 1994].

Furthermore, AOP is not just AspectJ. Other AOP implementations include AspectWerkz and JBoss-AOP. You might wish to take a look at these evolving technologies. Although we show some code samples for AspectJ, AOP has also been implemented in languages like C, C++, and C#. The truth of the matter is that the idea of keeping concerns separate pervades throughout all software development and is not limited to object-oriented systems alone.

In Chapter 1, “Problem to Attack,” we highlighted two specific kinds of crosscutting concerns that you need to keep separate—peers and extensions. Through some code samples in AspectJ, we show you how the above constructs help you keep such crosscutting concerns separate. Our intent is to show how you keep concerns separate, not to explain the details of AOP or AspectJ, so our examples are significantly simplified. As a developer, you might want to learn the specifics of AOP and AspectJ. The downloads along with additional code samples and a description of the AspectJ language extension to Java are available at the AspectJ Web site: http://www.eclipse.org/aspectj.

Keeping Peers Separate with Aspects

As a quick recap, peer concerns are distinct from each other, but their realizations overlap classes. Let’s consider the example of a Hotel Management System, which we use throughout the book. In particular, we discuss three specific functionalities in this example system: room reservation, customer check-in, and customer check-out. Briefly, the functionalities are as follows:

  • To reserve a room, you check the room availability, and if a room is available, you create a reservation.

  • To check in a customer, you assign him to a room and consume his reservation. At the same time, you create an initial bill for the customer.

  • To check out a customer, you collect the payment for the bill. Once the bill has been paid, the customer is removed from the room.

Figure 2-1 shows how these functionalities (represented horizontally) cut across classes in the system (shown as boxes). Each box lists the operations imposed on the respective classes.

Classes composed from different concerns.

Figure 2-1. Classes composed from different concerns.

Note that there are other operations not shown in Figure 2-1 that are common across the different functionalities. These operations are reusable, but our emphasis in Figure 2-1 is that the various functionalities impose different operations on the respective classes. As a developer, you need to collect all these responsibilities to implement each class. This is where crosscutting and, hence, tangling occurs. The Room class contains code needed by different peer functionalities. With AOP intertype declarations, you can indeed keep the specifics of each peer concern separate. Simplified Source Code to Check In Customer 0-1 shows the simplified listing that implements the Check In Customer functionality as an aspect using AspectJ.

Example 2-1. Simplified Source Code to Check In Customer

1.  public aspect CheckInCustomer {
2.    ...
3.    public void Room.assignCustomer ()
4.    {
5.      // code to check in customer
6.    }
7.    public void Reservation.consume()
8.    {
9.      // code to consume reservation
10.   }
11.   public void Payment.createBill()
12.   {
13.     // code to generate an initial outstanding bill
14.   }
15.   ...
16. }

Line 1 in Listing 2-1 declares CheckInCustomer as an aspect, as indicated by the aspect keyword. It contains a series of intertype declarations. There are two segments of an intertype declaration we want to highlight. The first segment is the name of an existing class, and the second segment is the existing operation that you want to add into. So, for instance, in line 3, you see

Room.assignCustomer()

This adds the operation assignCustomer() into the Room class. Notice that although this operation is part of the Room class, it is defined outside the Room class itself. Thus, aspect orientation escapes from the restriction on a class property needing to be defined within the traditional class modularity. This allows us alternate ways of organizing code—one that keeps the realization of peer concerns separate.

Keeping Extensions Separate with Aspects

Let us now look at how AOP can help you keep extensions separate. Extensions are add-ons to some base behavior. In this case, we have a base reserve room functionality, which we want to extend. Figure 2-2 shows a state chart describing the steps within a makeReservation operation. A user wants to make a reservation for a room from a certain date to a certain date. If there are no rooms available, the system displays a message indicating there are no rooms. If rooms are available, the system creates a reservation.

makeReservation operation.

Figure 2-2. makeReservation operation.

Suppose that you want additional functionalities (i.e., extensions) specifically to ensure that only authorized users are permitted to make a room reservation, and if there are no Rooms available, you want to put the user on a waiting list. This can be achieved in two steps:

  1. Identify the extension points in the existing operation in which the behavior needs to be extended.

  2. Define the additional behavior that will be used to extend the behavior at these extension points.

The extension points where you need to add or modify are marked as makeReservation operation. and makeReservation operation. for the authorization check and waiting list functionality, respectively. After composing the extension into the existing operation, you have the result in Figure 2-3. The shaded areas represent where updates have been made.

makeReservation modified with authorization and waiting list.

Figure 2-3. makeReservation modified with authorization and waiting list.

As shown in Figure 2-3, the additional behavior to check for authorization has been composed into the existing operation at marker makeReservation modified with authorization and waiting list.. At marker makeReservation modified with authorization and waiting list., instead of displaying a message indicating there are no rooms, the customer is put in a queue.

As you can see in Figure 2-3, the original makeReservation operation is now entangled with code needed to fulfill requirements not directly related to making reservations. Worse, it makes the makeReservation operation less understandable for the developer. But Figure 2-3 does represent the final composed behavior you want. If you want to analyze the performance over all other runtime characteristics, you must analyze the composed result, but you normally do so with a perspective that treats functionality generically. We discuss this issue in Part 4 of the book.

We now illustrate how AspectJ can be applied to keep the waiting list and authorization extensions separate. Listing 2-2 shows the code fragment for the reserve room functionality implemented in a class named ReserveRoomHandler.

Example 2-2. Simplified Source Code for ReserveRoomHandler

1.  class ReserveRoomHandler {
2.    ...
3.    public void makeReservation() throws NoRoomException
4.    {
5.      if(theRoom.getQuantityAvailable()<=0) {
6.          throw new NoRoomException () ;
7.      }
8.      createReservation() ;
9.    }
10.   ...
11. }

The ReserveRoomHandler class has an operation called makeReservation(), which as the name implies performs the reservation. It throws a NoRoomException if no rooms are available. The makeReservation() operation makes two calls:

  • getQuantityAvailable()This operation belongs to the Room class, and it returns the number of rooms available. If none are available, the makeReservation() operation throws a NoRoomException.

  • CreateReservation()This operation creates a reservation record in the data store if rooms are indeed available.

We look at how aspects in AOP help us keep the waiting list and the authorization extensions separate.

Handle Waiting List

Listing 2-3 shows the waiting list extension implemented as an aspect in AspectJ. The name of this aspect is HandleWaitingList (see line 1). It illustrates how aspects extend existing operations through pointcuts and advices. A pointcut identifies an execution point in the base, and an advice describes the additional behaviors that will run when execution reaches that point.

Example 2-3. Simplified Source Code to Handle Waiting List

1.  aspect HandleWaitingList {
2.    ...
3.    pointcut makingReservation() :
4.        execution(void ReserveRoomHandler.makeReservation()) ;
5.    ...
6.    after throwing (NoRoomException e) : makingReservation() {
7.      // code to add customer to waiting list ;
8.    }
9.  }

Lines 3 and 4 show a pointcut named makingReservation that refers to the execution of the makeReservation operation. Lines 6, 7, and 8 show an advice that occurs when the operation referenced by this pointcut throws a NoRoomException (i.e., when makeReservation() throws a NoRoomException).

The body of the advice adds the customer to the waiting list. For simplicity, we use comments in line 7 to denote this action.

Thus, AspectJ allows you to extend existing operations with additional behaviors. In this way, the original operation (i.e., makeReservation()) is not entangled with the extension. Thus, the extension is kept separate from the developer’s perspective. But that is not all: AspectJ allows us to extend multiple operations at once, as you shall see.

Check Authorization

Listing 2-4 implements the authorization extension. It applies not only to room reservation, but to any transactions that are performed on the system. You need to intercept all these transactions. Instead of specifying one pointcut for each transaction, AOP lets us use a limited form of pattern matching to specify multiple execution points at once. These execution points are collectively called performingTransaction, and we name the pointcut accordingly in lines 2, 3, and 4.

Notice the | | and * in the pointcut expression: | | refers to a logical or operator, and * is a wildcard to match names of packages, classes, operations, and so on. With the logical operators, we can combine multiple expressions to form a more complex pointcut. This is quite convenient to implement behaviors that impact a large number of operations. In lines 2, 3, and 4, the performingTransaction pointcut identifies all operations in the ReserveRoomHandler and CheckInHandler classes using the wildcard character *.

Example 2-4. Simplified Source Code to Handle Authorization

1.  aspect HandleAuthorization {
2.    pointcut performingTransaction ():
3.             call(void ReserveRoomHandler.*(..))
4.             || call(void CheckInHandler.*(..));
5.    void around () : performingTransaction() {
6.       if(isAuthorized()) {
7.          proceed() ;
8.       }
9.    }
10. }

Now, we want calls to ReserveRoom and CheckIn operations to be bypassed if the caller has no authorization. In AspectJ, this is achieved with an around advice, as depicted in lines 5 through 9. This advice is inserted at the performingTransaction pointcut. By default, the advice causes the execution of the existing operations in the ReserveRoomHandler or CheckInHandler to be bypassed (i.e., to go “around” them). The if condition in line 6 checks if there is sufficient authorization. If so, the advice invokes the existing operation using a proceed keyword (line 7); that is, you proceed with the existing operation.

The ReserveRoomHandler class in Listing 2-2 is totally free from any dealings with the waiting list or authorization extensions. This makes the ReserveRoomHandler class much easier to understand. In addition, all behaviors about the waiting list or authorization extensions are localized in their respective aspects: HandleWaitingList and HandleAuthorization. Thus, with aspects, you can indeed separate extensions from the base.

Note that Listing 2-2 has been simplified to make our discussion easier. In reality, you must pass the user object as a parameter into the around advice, and the isAuthorized() operation uses that parameter to perform the authorization check. Moreover, if the user has no authorization, you normally throw an exception as well. But all you need to know now is that the aspect-orientation technique provides an elegant means to keep extensions separate.

Our goal in this book is to identify, describe, and exemplify principles on how to apply aspect orientation effectively. We want to be as independent as possible of specific aspect-oriented implementations, and our proposed notation in Part III of can be mapped to the various existing implementations that we know of. Having a way to model aspects, we can proceed to discuss further principles on how to separate crosscutting concerns in Part IV of the book.

In this chapter, we limited our discussion to the programming part of aspect-orientation: AOP. Note that AOP is only part AOSD (aspect-oriented software development). AOP is about the programming part, whereas AOSD ranges from the requirements of some stakeholder concern (be they crosscutting or otherwise) to design, implementation, and test.

Need for Methodological Guidance

The composition mechanism in AOP gives you the ability to define behavior from outside a class. This is something relatively new to most of you. But a word of caution: we do not want you to be trigger-happy and poke advices into existing operations or add intertype declarations to existing classes in an uncontrolled manner. If you do so, you will end up with a patchwork system that is impossible to extend.

AOP provides a powerful composition mechanism for a reason: so you can achieve better separation of concerns and better modularity. Thus, you have to think about design and about when you should code behaviors into classes or aspects. To apply aspect orientation effectively, therefore, you need to know how to systematically identify aspects and classes. More accurately, the question should be how you identify concerns from which aspects and classes are derived.

In addition, keeping concerns separate does not mean that concerns are independent. For example, it is clear that waiting list functionality has dependencies on the room reservation functionality. This means that if the requirements for the latter changes, the former may also be impacted. You must be able to model concerns and their relationships. In this way you get a better understanding of stakeholder concerns and consequently can separate them better. The use-case technique provides such a method for you to model concerns and relationships, and the use-case–driven approach will help you drive the concerns all the way down to implementation.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.47.169