AspectJ


AspectJ is the most mature AOP language and currently the most widely used. It is an aspect-oriented extension to the Java language that supports the implementation of crosscutting code (i.e., aspects) as separate aspect modules. At a high level, AspectJ adds four kinds of program elements to Java: aspects, pointcuts, advice, and intertype declarations. Table 7-1 describes these elements and maps them to the corresponding design elements in Theme/UML. Of course, we look later at many details of AspectJ within these four constructs.

Table 7-1. High-Level Mapping of AspectJ Programming Elements to Theme/UML

Element

AspectJ

Theme/UML

Aspect

An aspect is a type that contains language elements that specify crosscutting structure and behavior. It may be instantiated, and it can be reasoned about at compile time.

Keyword: aspect

A crosscutting theme is a design equivalent to an aspect, though it may also contain behavior that is not crosscutting.

Pointcut

During the execution of a program, and as part of that execution's scope, there are points in the execution where behavior can be joined. These points are joinpoints. A pointcut is a predicate that can determine, for a given joinpoint, whether it is matched by the predicate.

Keyword: pointcut

Operation template parameters may be defined and referenced within sequence diagrams representing points in the execution of the sequence of behavior where behavior can be joined. These templates are replaced by (possibly multiple) actual operations as specified in the bind[] attachment. Template specifications and their matching bind[] specifications are therefore equivalent to pointcuts.

Advice

A piece of advice is code that executes at a joinpoint, using some of the execution scope.

Keywords: before, after, around

Within a sequence diagram, crosscutting behavior may be specified to execute when a template operation is called. This behavior is equivalent to advice code.

Intertype declarations

An intertype declaration is a programming element, such as an attribute, constructor, or method, that is added to a type that may add to or extend that type's structure.

Design elements that are not template elements may be defined within crosscutting themes. These may be classes, attributes, operations, or relationships and are equivalent to elements that are added to (merged with) base theme elements.


From the perspective of mapping your Theme/UML models to AspectJ, you must consider the base themes and crosscutting themes somewhat differently. The AspectJ model for aspects is primarily concerned with separating and weaving crosscutting behavior onto a single base. It is, however, possible to maintain a separation of the base themes in AspectJ (discussed later) but first, we examine mapping the crosscutting themes to AspectJ's aspects.

In general, we advise you to define an abstract aspect for each of your crosscutting themes and a corresponding concrete aspect to capture the binding specification to the base themes. Figure 7-1 illustrates Table 7-1 in a little more detail (though still at a high level) and shows the general approach we take to mapping Theme/UML design models to AspectJ constructs. It especially illustrates a separation into abstract and concrete aspects. As in previous chapters, shadows are used to indicate mapping between elements.

Figure 7-1. Map to implementation.


Overview of Steps

In this section, we provide general principles for mapping the main Theme/UML design constructs to the main AspectJ constructs. Following is a set of guidelines for deriving an abstract aspect. Don't worry if some of the steps aren't immediately clearwe work through them in detail next.

  1. Declare an abstract aspect to represent the crosscutting theme.

  2. For each class with templates in the crosscutting theme:

    2.1. Declare an interface in the abstract aspect.

    2.2. For each template operation that initiates a sequence diagram:

    2.2.1. Declare a corresponding abstract pointcut.

    2.2.2. The abstract pointcut should have the following formal parameters:

    One to capture the target object.

    One for each of the specified formal parameters on the template operation.

    2.2.3. Declare before, after, and around advice relative to the sequence of behavior defined in the sequence diagram.

    2.3. For each template operation listed after the template operation that initiates the sequence diagram:

    2.3.1. Declare a corresponding abstract method on the class's interface.

    2.4. For each operation in the class that is not defined as a template:

    2.4.1. Implement as an intertype declaration against the class's interface.

    2.5. Each attribute maps to an intertype declaration on the class's interface.

    2.6. Each association from the class to another class maps to an intertype declaration on the class's interface.

  3. Any other class (i.e., those without template operations) should be implemented directly if not already present. If a class already exists in the base with which the crosscutting theme's specification is intended to merge, then the crosscutting theme's specification should be implemented as intertype declarations on that class.

Mapping to a concrete aspect is straightforwardyou extend the abstract aspect and provide concrete pointcuts from the bind[] specifications (watching out for control flow restrictions) and concrete implementations for the intertype declarations. We now demonstrate the general policies useful for implementing your system using AspectJ by working through mapping the track-energy and P2PCommunication crosscutting themes.

Track-energy

The track-energy crosscutting theme[4] provides a design for three crosscutting behavior specifications and some additional time-related behavior. This specification yields an abstract aspect with a combination of interfaces, aspect attributes and methods, abstract pointcuts, and advice. We also look at how we can define a concrete aspect that extends the abstract aspect, and map the bind[] specification of base operations to the pointcuts.

[4] Refer to Chapter 5 for the design and Chapter 6 for the composition specification

Abstract Aspect

As defined in the AspectJ documentation, an aspect is a new kind of type for crosscutting behavior that encapsulates all the elements required by an aspectthat is, pointcuts, advice, and intertype declarations. An abstract aspect allows you to provide as much of the code needed by the aspect as is possible without directly referring to the base code that it will crosscut. This is directly analogous to what a crosscutting theme does in that a crosscutting theme provides the design of crosscutting behavior without direct reference to the base theme(s) it will crosscut. AspectJ provides the keyword aspect to allow you to define your aspect, which you can qualify with the abstract keyword. From the track-energy crosscutting theme, we can therefore declare an abstract aspect as follows:

 abstract aspect TrackEnergy { } 

Note that we have changed the style of the theme name for the aspect to naming conventions more common to classes than to packages. Aspects in AspectJ are more closely related to classes than to packages, and so we move to the appropriate convention. We recommend that the name you give the abstract aspect be strongly related to the name of the Theme/UML design theme for traceability purposes.

Interfaces

Within the track-energy theme design, two classes are defined and used to achieve the theme's goals: these classes are the Energy class and the Game class. In essence, these classes are placeholder classes to be merged with base classes, supplementing the base classes with additional structure and behavior. In AspectJ, there are two possible options to handle thiswe could define a class or an interface for each. If we use a class, an inheritance relationship must be defined for the concrete class, because the crosscutting theme's class may name operations that are essentially abstract in the aspect. However, a base class may already have a superclass, which would cause us difficulties as multiple inheritance is not possible in Java. Defining an interface for each class is therefore a better idea, and we can add a specification of which base classes implement these interfaces to the concrete aspect later. For the track-energy theme, the AspectJ code for defining interfaces for the Energy and Game classes follows.

 interface EnergyEntityI{Game getGame();} interface GameI{boolean isPlayerInLocation();                 boolean isActive();} 

Notice that each interface also has an operation defined. If you use operations that you expect to be implemented in a base class, then you can list them in the interface, giving a context for the compilation of your aspects. For the track-energy theme, we need a reference to the relevant game object (obtained using getGame()), because the Game class is responsible for figuring out the amount of energy loss or gain as a result of a particular action. In addition, the Game knows about game locations, and so we assume it provides us with the implementation of isPlayerInLocation() that is referenced in the design (refer to Figure 5-14 in Chapter 5, "Theme Design"), used to tell us whether a player is in a game location. We have also added an isActive() method interface that we can query to determine whether the game is still running (used later by a thread that manages the time-based loss of energy).

Your crosscutting theme's classes may also require additional interfaces not explicitly defined in the design. For example, the Energy class responds to a signal loseEnergy() that is sent periodically. If you implement this using threads (which seems like a good idea), this means that the Energy class should implement Java's Runnable interface for handling threads. We can declare this in the aspect using the standard Java extends keyword:

 interface EnergyEntityI           extends Runnable {Game getGame();} 

Aspect Attributes

From the design of the track-energy theme, we can see that the Energy class is specified as having three attributes, which we can define on the interface. The initialization values are taken from the class model in the theme design.

 // aspect attributes int EnergyEntityI.duration = 5; int EnergyEntityI.energyUnit = 10; int EnergyEntityI.changeAmount = 1; 

In the crosscutting theme design, the Energy class also has a relationship to the Game class. It looks as if we've ignored it, as it might have been mapped to an aspect attribute. If you're trying to decide whether an attribute or a relationship such as this should be defined as an aspect attribute, you should consider your expectations of the base class. If you think that it is reasonable to expect, for example, that base entities in the game will have references to a game object, then we can exclude game as an aspect attribute for trackEnergy. In this implementation, we assume there is a getGame() method in the class that implements the EnergyEntityI interface that we can use when we need a reference to the game. Consequently, we do not need to define it as an aspect attribute here.

Aspect Methods

In general, the process of moving from method designs to method code in AspectJ is no different than how you would move to any Java implementation from a standard UML design, with one exception. Each method should be defined in the context of the particular interface to which it is attached.

Remember from the design that we intend the classes in the crosscutting theme to be merged with classes in the base themes. We can achieve this by attaching the methods to the relevant interfaces, causing any base classes that implement the interface to implicitly have the methods added to them.

The track-energy theme defines a number of methods that do the work of tracking energy: setChangeAmount(), changeEnergy(), loseEnergy(), and getEnergy(). Each method may be implemented in the abstract aspect as follows:

 private void EnergyEntityI.setChangeAmount(int a)  { changeAmount = a; } private void EnergyEntityI.changeEnergy(int e)  { energyUnit += e; } private void EnergyEntityI.loseEnergy()  { energyUnit -= changeAmount; } private void GameI.getEnergy(String name,                              EnergyEntityI energyEntity)  {    if(name.equals("completeWizardErrand"))    {  energyEntity.changeEnergy(4); }    else if(name.equals("completeWarriorTest"))    { energyEntity.changeEnergy(3); }    //... and so on  } 

You must also consider whether there are further methods that the aspect requires as a result of particular implementation decisions you've made. An example is a run() method, which is required because we declared that the EnergyEntityI interface extends Runnable. While the game is active, we want an energy entity to periodically lose energy. The run() method is implemented as follows:

 public void EnergyEntityI.run()  {    GameI game = getGame();    while(game.isActive())    {       try {        loseEnergy();        Thread.sleep(5000);     } catch (InterruptedException e) {       // Log or notify exception occurred       e.printStackTrace();     }   } } 

Abstract Pointcuts

In AspectJ, a pointcut describes points of execution in a program where crosscutting behavior is required. In Theme/UML, template operations initiate a sequence of behavior defined in a sequence diagram. These template operations represent a placeholder for base operations where crosscutting behavior is required. The two concepts are analogous for operations, making AspectJ's pointcuts a good match to represent such template operations. The track-energy theme has three examples: moveLocation, energyAction, and joinGame. As the pointcuts are being used in an abstract aspect, we choose to also define the pointcuts as abstract:

 public abstract pointcut moveLocation(EnergyEntityI energyEntity); public abstract pointcut energyAction(EnergyEntityI energyEntity); public abstract pointcut joinGame(EnergyEntityI energyEntity); 

We can override these pointcuts in the concrete aspect later. Note that each pointcut has one parameter of type EnergyEntityI. When pointcuts have a parameter, any advice (described in the next section) that uses this pointcut can reference this parameter, which is read from the context of the currently executing joinpoint. A reference to an instance that implements EnergyEntityI is required for the implementation of crosscutting behavior for each pointcut, as you will see later.

Advice

In AspectJ, advice is the implementation of behavior that crosscuts the set of execution points defined by the pointcut. This makes advice an obvious construct to use to implement the sequence of behaviors defined in sequence diagrams in the crosscutting themes. Advice can be defined to execute before, after, or around the execution points defined by pointcuts.

From the Theme/UML designs, you may remember that in some cases, we added _do_ to the front of the names of the template operations that initiate a sequence of behavior. We did this within the sequence of execution to indicate the execution of the base operation to which the crosscutting behavior is to be applied. This is analogous to the execution points defined by operation pointcuts and so we can examine the sequence diagrams to assess whether the crosscutting behavior happens before, after, or around the template operation starting with _do_. When to choose before or after is clear from the timeline of the sequence of operations. Around advice, which essentially replaces the base operation, will be obvious from any conditional specifications of operation execution defined within the sequence diagram.

From the track-energy theme, the sequence diagram for the moveLocation template operation indicates that after the base operation is executed, the game object is asked whether the energy entity is in a game location and, depending on a true or false answer, sets the amount that affects the periodic energy loss to 1 or 2 energy units. This behavior is captured in the following after advice. Notice that this code requires a reference to the energy entity, which is why we needed to define energyEntity as a parameter in the abstract pointcut.

 after(EnergyEntityI energyEntity) returning:                          moveLocation(energyEntity) {    GameI game = energyEntity.getGame();    if(game.isPlayerInLocation())    {  energyEntity.setChangeAmount(2);           } else    {  energyEntity.setChangeAmount(1);           } } 

The sequence diagram for the energyAction template operation indicates that after the base operation is executed, the game object is asked to figure out the impact on the energy entity's energy as a result of the execution of the template operation. We can use AspectJ's thisJoinPoint construct to obtain a String representation of the name of the base method being crosscut, which the getEnergy() method uses to determine the energy impact, as follows.

 after(EnergyEntityI energyEntity) returning:                             energyAction(energyEntity) {    String currentActionName = thisJoinPoint.getSignature().getName();    GameI game = energyEntity.getGame();    game.getEnergy(currentActionName, energyEntity); } 

The sequence diagram for the joinGame template operation (refer to Figure 5-19) indicates that after the base operation is executed, the thread to handle the loss of energy should be started. You saw the corresponding run() method earlier.

 after(EnergyEntityI energyEntity) returning:                     joinGame(energyEntity) {    Thread energyLossThread = new Thread(energyEntity);    energyLossThread.start(); } 

We now have the full abstract aspect code to implement the track-energy theme, as illustrated in its entirety in Listing 7-1.

Listing 7-1. Abstract Aspect
 abstract aspect TrackEnergy {   // interfaces   interface EnergyEntityI extends Runnable {Game getGame();}   interface GameI{boolean isPlayerInLocation();                   boolean isActive();}   // aspect attributes   int EnergyEntityI.duration = 5;   int EnergyEntityI.energyUnit = 10;   int EnergyEntityI.changeAmount = 1;   // aspect methods   private void EnergyEntityI.setChangeAmount(int a)   { changeAmount = a; } private void EnergyEntityI.changeEnergy(int e) { energyUnit += e; } private void EnergyEntityI.loseEnergy() { energyUnit -= changeAmount; } private void GameI.getEnergy(String name,                              EnergyEntityI energyEntity) {    if(name.equals("completeWizardErrand"))    {  energyEntity.changeEnergy(4); }    else if(name.equals("completeWarriorTest"))    {  energyEntity.changeEnergy(3); }    //... and so on } public void EnergyEntityI.run() { GameI game = getGame();    while(game.isActive())    {       try {          loseEnergy();          Thread.sleep(5000);       } catch (InterruptedException e) {          // Log or notify exception occured          e.printStackTrace();       }    }    } // abstract pointcuts public abstract pointcut moveLocation(                          EnergyEntityI energyEntity); public abstract pointcut energyAction(                          EnergyEntityI energyEntity); public abstract pointcut joinGame(                          EnergyEntityI energyEntity); // advice after(EnergyEntityI energyEntity) returning:                              moveLocation(energyEntity)  {     GameI game = energyEntity.getGame();     if(game.isPlayerInLocation())     {  energyEntity.setChangeAmount(2);   }     else     {  energyEntity.setChangeAmount(1);   }  }  after(EnergyEntityI energyEntity) returning:                      energyAction(energyEntity)  {     String currentActionName =            thisJoinPoint.getSignature().getName();     GameI game = energyEntity.getGame();     game.getEnergy(currentActionName, energyEntity);  }  after(EnergyEntityI energyEntity) returning:                      joinGame(energyEntity) {     Thread energyLossThread = new Thread(energyEntity);     energyLossThread.start();  } } 

Concrete Aspect

Now that we've implemented the abstract aspect, we need to think about how we implement the binding specification of the composition relationship's bind[] attachment. In AspectJ, concrete aspects extend an abstract aspect in a manner similar to Java classes, and so we can use them to implement the base operations to be bound to the template operations (or, the pointcuts, in AspectJ parlance). As it turns out, mapping to a concrete aspect is relatively straightforward, and we work through the steps with the track-energy theme and its binding illustrated in Figure 7-1 and copied here:

 bind[ < Player.setLocation()>        < Player.{incrementCrystals(), addCrystals(),                  completeWizardErrand(), completeWarriorTest() } >        < Player.joinGame() > ] 

Extending Abstract Aspect

AspectJ uses the same extends construct as Java to extend an abstract aspect. For the TRackEnergy aspect, this looks as follows:

 public aspect ConcreteTrackEnergy extends TrackEnergy { } 

Binding to the Aspect's Interfaces

The crosscutting theme's classes, Energy and Game, are represented through interfaces declared on the abstract aspect. As we described in Chapter 6, we want to merge the base classes with the classes defined using interfaces in the aspect. The intertype declarations mechanism we used in the abstract aspect effectively merges the methods and attributes defined in the aspect's interface with a base class that implements that interface, which is what we need. We can code this using the declare parents construct from AspectJ as follows:

 declare parents: Player implements EnergyEntityI; declare parents: Game implements GameI; 

So, how do you decide which base classes implement EnergyEntityI and GameI from the theme design? Deciding on Player for EnergyEntityI was easy. When we look at the bind[] attachment, we see that the Player class contains all the methods that replace both the moveLocation and the energyAction templates. Both these templates initiate behavior on the Energy class in the theme, which we mapped to the EnergyEntityI interface. As such, it makes sense that the Player class implements EnergyEntityI. Deciding on Game for GameI appears, on the surface, to be straightforward because they have the same name, and Theme/UML defaults to same-name merge for crosscutting theme composition (refer to Chapter 6). However, it is possible that a base theme's class and a crosscutting theme's class have the same name but should not be merged, as we discussed in Chapter 6. In this case, the designer so indicates in the Theme/UML models with further composition relationships that indicate which base classes should be merged with the classes in the crosscutting theme.

Binding to Concrete Pointcuts

The abstract aspect declared abstract pointcuts that also must be made concrete. Each abstract pointcut corresponds to a template operation in the design model, and you can tell which base operations replace the templates (and therefore should be concrete pointcuts) from the bind[] attachment. As follows, the concrete pointcuts capture the execution of the concrete operations and expose the target object and arguments of each. The sequence diagrams did not indicate any control flow restrictions, so we do not have to consider cflow or cflowbelow here.

 pointcut moveLocation(EnergyEntityI energyEntity):          this(energyEntity) &&          execution (* Player.setLocation(..)); pointcut energyAction(EnergyEntityI energyEntity):          this(energyEntity) &&          (execution (* Player.incrementCrystals(..)) ||          execution (* Player.addCrystals(..)) ||          execution (* Player.completeWarriorTest(..)) ||          execution (* Player.completeWizardErrand(..))); pointcut joinGame(EnergyEntityI energyEntity):          this(energyEntity) &&          execution (* Player.joinGame(..)); 

Other Concrete Implementations

You may also need to consider whether it is necessary to provide concrete methods or advice, some of which may override methods/advice in the abstract aspect. Though we don't have any examples in the track-energy theme, it is supported by AspectJ and may be required for some implementation reason. In general though, we think it's better to, where possible, keep the aspect code in the abstract aspect and the concrete mappings to the interfaces and pointcuts in the concrete aspect.

We now have the full concrete aspect code to implement the bindings to the track-energy theme, as illustrated in its entirety in Listing 7-2.

Listing 7-2. Concrete Aspect
 public aspect ConcreteTrackEnergy extends TrackEnergy {  declare parents: Player implements EnergyEntityI;  declare parents: Game implements GameI;  pointcut moveLocation(EnergyEntityI energyEntity):           this(energyEntity) &&           execution (* Player.setLocation(..));  pointcut energyAction(EnergyEntityI energyEntity):           this(energyEntity) &&           (execution (* Player.incrementCrystals(..)) ||           execution (* Player.addCrystals(..)) ||           execution (* Player.completeWarriorTest(..)) ||           execution (* Player.completeWizardErrand(..)));  pointcut joinGame(Player player):           this(player) &&           execution (* Player.joinGame(..)); } 

P2PCommunication

The P2PCommunication crosscutting theme provides a design for four crosscutting behavior specifications and, similarly to the track-energy theme, some additional time-related behavior to be implemented in a separate thread. Again, we can define an abstract aspect for the P2Pcommunication behavior designed in Theme/UML, and a concrete aspect to capture the bind[] specification, and other concrete code.

Abstract Aspect

After following the same steps as we did for the track-energy theme, we get the abstract aspect illustrated in Listing 7-3. There are some interesting details to point out here. First, Line 8 uses the AspectJ declare precedence construct that we have not met previously. When you are applying more than one aspect to your base code, you may state which should be executed first with this construct. If we look at the design of both the P2PCommunication theme and the track-energy theme, we can see that both have a joinGame pointcut. If the concrete aspects for both name the same concrete pointcuts that capture the same points of execution (as is likely), then both advices will be executed. We have decided that it makes more sense to ensure that a player requests the full game state prior to starting the clock on losing energy. Though not illustrated in Chapter 6, it is likely that the ordering of this composition would be clear as track-energy would be composed with the result of the composition of the base game with the P2PCommunication theme.

Another interesting difference with this aspect is that some of the behavior that belongs in this theme has been encapsulated in another class. See Line 11, which references a GameComms class. At a general level, Theme/UML does not prescribe any minimum or maximum size (in terms of number of classes, methods, etc.) for a themeit should be whatever size is necessary to handle the requirement(s) it supports. At the design level, this is not an issue, as we can use multiple classes to provide sensible modularizations. If you think that the aspect is getting too large, it might be possible to further modularize some functionality into a separate class (or indeed aspect). We illustrate this approach with the GameComms classeverything to do with the actual broadcasting of events has been removed from the aspect into this class. Indeed, the P2PCommunication aspect has turned out to be quite large, and so it is a good candidate for further refactoring into different classes or aspects. For example, you could have multiple concrete aspects: one to handle changes to the game, one to handle player-state change, one to handle location-state change, and so on.

The remainder of the P2PCommunication abstract aspect code follows in a straightforward manner from the steps described in detail for the track-energy theme. Lines 17 to 127 provide the methods for the aspect, including an implementation of the run() method (1725). Marshalling and unmarshalling the game state is illustrated in some detail in lines 38 to 103, while only skeleton code is illustrated for marshalling and unmarshalling player and location state in lines 105 to 121. Abstract pointcuts to map to the four template operations are in lines 130 to 133, with corresponding after advice from lines 136 to 161.

Listing 7-3. P2PCommunication Abstract Aspect
 1     import java.util.Collection; 2     import java.util.Iterator; 3 4     abstract aspect P2PCommunication { 5 6     // Inter-type declarations 7     declare parents: GameAdmin implements Runnable; 8     declare precedence: P2PCommunication, TrackEnergy; 9 10    aspect attributes 11    private GameComms Game.gameComms = GameComms.getInstance(); 12 13    // aspect methods 14    public GameComms Game.getGameComms() 15    {  return gameComms; } 16 17    public void GameAdmin.run() 18    { 19       while (true) { 20          try { checkAvailableGames(); 21                Thread.sleep(5000); 22          } catch (InterruptedException e) { 23                // Log or notify exception occured } 24       } 25    } 26 27    public void Game.fullGameStateRequest(String gameName) 28    { 29       GameState gameState = marshallGame(); 30       GameComms gameComms = getGameComms(); 31       gameComms.publishGameState(gameState); 32    } 33 34    public void Game.fullGameState(GameState gameState) 35    {  unMarshallGame(gameState);  } 36 37 38    public GameState Game.marshallGame() 39    { 40       GameState gameState = new GameState(); 41       // get game location states and add them to game state 42       Collection gameLocations = getLocations(); 43       Iterator gameLocationsIteration = gameLocations.iterator(); 44       while(gameLocationsIteration.hasNext()) 45       { 46          Location location = (Location)gameLocationsIteration.next(); 47          LocationState locationState = location.marshallState(); 48          gameState.addLocationState(locationState); 49       } 50       // get game player states and add them to game state 51       Collection gamePlayers = getPlayers(); 52       Iterator gamePlayersIteration = gamePlayers.iterator(); 53       while(gamePlayersIteration.hasNext()) 54       { 55          Player player = (Player)gamePlayersIteration.next(); 56          PlayerState playerState = player.marshallState(); 57          gameState.addPlayerState(playerState); 58       } 59       // set the duration remaining in the game 60       gameState.setDuration(getGameDuration()); 61       // set the throne room 62       Location throneRoom = getThroneRoom(); 63       gameState.setThroneRoom(new LocationState(throneRoom)); 64       //........ 65       return gameState; 66    } 67 68    public synchronized void Game.unMarshallGame(                                GameState gameState) 69    { 70       Collection gameLocations = getLocations(); 71       Collection locationsStates = gameState.getPlayersStates(); 72       Iterator gameLocationsIterator = gameLocations.iterator(); 73       Iterator locationsStatesIterator =                   locationsStates.iterator(); 74 75       while(gameLocationsIterator.hasNext()) 76       { 77          Location location = (Location)gameLocationsIterator.next(); 78          while(locationsStatesIterator.hasNext()) 79          { 80          LocationState locationState = 81             (LocationState)locationsStatesIterator.next(); 82          if(location.getName().equals(locationState.getName())) 83             { location.unMarshallState(locationState); } 84          } 85       } 86 87       Collection gamePlayers = getPlayers(); 88       Collection playerStates = gameState.getLocationsStates(); 89       Iterator gamePlayersIterator = gamePlayers.iterator(); 90       Iterator playerStatesIterator = playerStates.iterator(); 91 92       while(gamePlayersIterator.hasNext()) 93       { 94          Player player = (Player)gameLocationsIterator.next(); 95          while(playerStatesIterator.hasNext()) 96          { 97             PlayerState playerState = 98             (PlayerState)playerStatesIterator.next(); 99             if(player.getName().equals(playerState.getName())) 100          { player.unMarshallState(playerState); } 101         } 102      } 103   } 104 105   public PlayerState Player.marshallState() 106   { 107      PlayerState playerState = new PlayerState(this); 108      return playerState; 109   } 110 111   public void Player.unMarshallState(PlayerState playerState) 112   { update(playerState); } 113 114   public LocationState Location.marshallState() 115   { 116      LocationState locationState = new LocationState(this); 117      return locationState; 118   } 119 120   public void Location.unMarshallState(                   LocationState locationState) 121   { update(locationState); } 122 123   public synchronized void GameAdmin.checkGamesAvailable() 124   { 125      Collection activeGames = marshallActiveGames(); 126      crystalGamesAvailable(activeGames); 127   } 128 129   // Pointcuts 130   abstract pointcut newGameAdmin(GameAdmin gameAdmin); 131   abstract pointcut joinGame(Player player); 132   abstract pointcut pStateChange(Player player); 133   abstract pointcut lStateChange(Location location); 134 135   // Advice 136   after(Player player) returning: joinGame(player) 137   { 138      Game gamePlayerIsPlaying = player.getGame(); 139      gamePlayerIsPlaying.fullGameStateRequest( 140                             gamePlayerIsPlaying.getName()); 141   } 142   after(Player player)returning: pStateChange(player) 143   { 144      PlayerState playerState = player.marshallState(); 145      Game game = player.getGame(); 146      GameComms gameComms = game.getGameComms(); 147      gameComms.publishPlayerState(playerState); 148   } 149   after(Location location)returning: lStateChange(location) 150   { 151      LocationState locationState = location.marshallState(); 152      Game game = location.getGame(); 153      GameComms gameComms = game.getGameComms(); 154      gameComms.publishLocationState(locationState); 155 156   } 157   after(GameAdmin gameAdmin)returning: newGameAdmin(gameAdmin) 158   { 159      Thread checkAvailableGameThread = new Thread(gameAdmin); 160      checkAvailableGameThread.start(); 161   } 162} 

Concrete Aspect

Abstract aspects must be specified concretely to be instantiated and to operate. Part of specifying the concrete aspect is defining the joinpoints where the advice should happen. To do this, each abstract pointcut declared in the aspect must be made concrete. Since each abstract pointcut corresponds to a template operation in the design model, you can tell which base operations should become concrete pointcuts from the matching specification in the bind[] attachment, ordered in the same way as the template box on the crosscutting theme. Mapping the bind[] specification for the P2PCommunication crosscutting theme to a concrete aspect is, again, similar to that described for track-energy. Referring back to Figure 6-26, the bind[] specification was:

 bind[ < Player.joinGame()>       < Player.{incrementCrystals(), addCrystals(),                 decrementCrystals(), addMagicItem(),                 deleteMagicItem() } >       < Location.{setCrystals(), takeCrystals() } >       < GameAdmin.GameAdmin()> ] 

As shown in Listing 7-4, the concrete pointcuts capture the execution of the concrete operations and expose the target object and arguments of each.

Listing 7-4. P2PCommunication Concrete Aspect
 1   public aspect ConcreteP2PCommunication extends P2PCommunication { 2 3      pointcut newGameAdmin(GameAdmin gameAdmin): this(gameAdmin) && 4               execution(GameAdmin.new(..)); 5 6      pointcut joinGame(Player player): this(player) && 7               execution(* Player.joinGame(..)); 8 9      pointcut pStateChange(Player player): this(player) && 10               (execution(* Player.incrementCrystals(..)) || 11               execution(* Player.decrementCrystals(..)) || 12               execution(* Player.addCrystals(..)) || 13               execution(* Player.addMagicItem(..)) || 14               execution(* Player.deleteMagicItem(..))) ; 15 16      pointcut lStateChange(Location location): this(location) && 17               (execution(* Location.setCrystals(..)) || 18               execution(* Location.takeCrystals(..))); 19   } 

Base Themes

In the Crystal Game design, we have multiple themes that are base themes, in addition to the crosscutting themes. This appears to contrast somewhat with the AspectJ model of a single base. However, it is possible to maintain the symmetrical base-theme separation using AspectJ's intertype declaration facilities. To achieve this, for each base theme, you implement aspects that contain intertype declarations for the structure and behavior defined in the theme. Each such aspect will be different from the aspects we've looked at so far in that it will not contain any pointcuts or advice, as there is no crosscutting behavior. You then specify a "base" program with skeleton definitions of the relevant classes, and specify concrete classes to which the intertype declarations in the aspects will be added. See Figure 7-2 for an illustration for the start, enter-location and duel themes.

Figure 7-2. AspectJ base themes.


Each theme is modularized into its own namespace, with an aspect for each class's intertype declarations. Themes that work with the same classes each have an aspect for that class's intertype declaration in its corresponding namespace. Listings 7-5, 7-6, and 7-7 illustrate the methods' signatures[5] and attributes that are relevant for the Player class in the start, enter-location and duel themes, with Listing 7-8 illustrating the stub Game.Player class.

[5] Method code excluded for brevity.

Listing 7-5. Start Theme Player
 1   public aspect Player 2   { 4      private String Game.Player.name; 5      private String Game.Player.ipAddress; 6      private Location Game.Player.location; 7      private Game Game.Player.game; 8 9      public void Game.Player.setPlayerslocation(                    Location location) 10      public Game.Player.new(String name) 11      public String Game.Player.getName() 12      public Game Game.Player.getGame() 13      public Location Game.Player.getLocation() 14      public void Game.Player.joinGame(Game game) 15   } 

Listing 7-6. Enter-Location Theme Player
 1   public aspect Player { 2      private GPSComponent Game.Player.gpsComponent; 3 4      public void Game.Player.setLocation(Location location) 5      public Collection Game.Player.checkOtherPlayers() 6      public void Game.Player.takeCrystals() 7      public void Game.Player.takeMagicItems() 8      public void Game.Player.setMagicItem(MagicItem item) 9      public void Game.Player.setMagicItems(Collection magicItems) 10     public void Game.Player.newLocation(Location location) 11   } 

Listing 7-7. Duel Theme Player
 1   public aspect Player 2   { 3      private int Game.Player.crystals; 4      private boolean Game.Player.crystalsOffered; 5      private boolean Game.Player.wagerAccepted; 6      private boolean Game.Player.wagerAgreed; 7      private boolean Game.Player.finishedRecieved; 8      private String Game.Player.rockPaperScissors; 9      private Collection Game.Player.magicItems =                 new LinkedList(); 10 11      public void Game.Player.getMagicItems() 12      public String Game.Player.getRockPaperScissors() 13      public int Game.Player.getCrystals() 14      public void Game.Player.duel() 15      public void Game.Player.duelLoser() 16      public void Game.Player.addCrystals(int numberOfCrystals) 17      public void Game.Player.decrementCrystals() 18      public void Game.Player.incrementCrystals() 19      public void Game.Player.addMagicItem(MagicItem magicItem) 20      public void Game.Player.deleteMagicItem(MagicItem magicItem) 21      public void Game.Player.updateRPS(String rockPaperScissors) 22      public void Game.Player.magicSelected(MagicItem magicItem) 23      public void Game.Player.wagerTwoCrystals() 24      public void Game.Player.wagerMagicItem(MagicItem magicItem) 25      public void Game.Player.wagerTwoMagicItems 26          (MagicItem firstMagicItem, MagicItem firstMagicItem) 27      public Wager Game.Player.wagerCrystal() 28      public boolean Game.Player.wagerAccepted(Wager wager) 29      public String Game.Player.duelRevealRPS 30          (Player player, String rpsSelection) 31   } 

Listing 7-8. Player (in Game Project)
 1   public class Player 2   { 4      // empty stub class into which the aspect intertype 5      // declarations are woven 6   } 

You cannot have clashing method or attribute names in the different aspects to be woven with the same base class. We had one example in the game: the duel() method, which was defined in both the enter-location and the duel themes. If this occurs, then you must decide whether the methods are actually different and therefore could be renamed to avoid a clash or whether the method is really the same and therefore should be implemented in only one theme's namespace. The Theme/UML design guides us in the decision here, as the composition specification that includes duel and enter-location (refer to Figure 6-21) states that the duel() method in the duel theme should override the one in the enter-location theme. Because of this, we provide the implementation of the duel() method in the Duel project.

Taking this approach to intertype declarations for themes, you will have a different namespace for every theme in your design. This has the advantage of giving you traceability from your requirements to your Theme/Doc and Theme/UML models through to your code.

Alternatively, you could work with a design made up of the composition of base themes, and apply standard object-oriented programming techniques. In this case, you end up with a set of standard object-oriented classes that have the different themes tangled together. This is the base to which the crosscutting aspects will be applied as we have already seen.



Aspect-Oriented Analysis and Design(c) The Theme Approach
Aspect-Oriented Analysis and Design: The Theme Approach
ISBN: 0321246748
EAN: 2147483647
Year: 2006
Pages: 109

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net