Part 1 - Overview and Process

Java Development News:

Part 1 - Overview and Process

By Ramnivas Laddad

01 Dec 2003 | TheServerSide.com

Introduction

Refactoring, a process and a set of techniques to reorganize code while preserving the external behavior, has gained popularity due to its practical value in creating agile code. Recently, aspect-oriented programming (AOP) has received increased attention due to its power in encapsulating crosscutting concerns in a system through use of a new unit of modularity called an aspect. Aspect-oriented refactoring (AO refactoring) synergistically combines these two techniques to refactor crosscutting elements. In this two-part series, we will examine the fundamentals of AO refactoring, the steps involved in the process, and a few common techniques.

Individually, refactoring and AOP both share the high-level goal of creating systems that are easier to understand and maintain without requiring huge upfront design effort. A combination of the two, aspect-oriented refactoring helps in reorganizing code corresponding to crosscutting concerns to further improve modularization and get rid of the usual problem symptoms: code-tangling and code-scattering.

Aspect-oriented refactoring goes beyond conventional refactoring techniques. While steps in conventional refactoring modularize code into a clean OO implementation, the use of AOP squeezes out code that cannot be further refactored. Aspect-oriented refactoring offers substantial improvement in a variety of situations such as exception-handling policies, local contract enforcements, concurrency control, and worker object creation.

Aspect-oriented refactoring offers many benefits. Initially, the attractive part of aspect-oriented refactoring is implementation of the same functionality with fewer lines of code. However, after a short duration of continued use, aspect-oriented refactoring shows its real benefit in code that is easy to understand, highly consistent, and simple to change.

Throughout the article, I use AspectJ for illustrations. However, most techniques can be ported to other AOP systems such as AspectWerkz and JBoss/AOP. This article series assumes familiarity with AspectJ syntax. Please refer to the "Resources" section for places you can get yourself acquainted with AspectJ.


The Fundamentals

AO refactoring provides additional means to conventional refactoring techniques. Consider the "Extract method" for refactoring, which encapsulates logic into a separate method, while leaving calls to the method in potentially multiple places. With the AO refactoring technique of "Extract method calls", you can take an additional step and refactor out even those calls into a separate aspect. AO refactoring also offers a few new techniques of its own. For example, "Extract exception handling" refactors out try/catch blocks and any exception handling code into a separate aspect, in the process saving a significant amount of code as well as creating highly consistent and maintainable code.

Let's consider a concrete example of the aspect-oriented refactoring technique called "Extract method calls". It encapsulates the crosscutting functionality by isolating method calls into a separate aspect. There are many occasions for such refactoring: class-level logging, security permission checks, persistence session management (such as openSession() and closeSession() calls while using Hibernate). Figure 1 shows how "Extract method calls" augments the "Extract method" technique.



(Click to see full scale image)

Figure 1: Aspect-oriented refactoring augmenting conventional refactoring. The "Extract method calls" refactoring encapsulates calls to a method from multiple places into an aspect.


In figure 1, the initial implementation has a duplicated piece of code in multiple places. The "Extract method" refactoring encapsulates the duplicated logic in a new method and replaces each original piece of code with a call to the new method. Then, the aspect-oriented refactoring of "Extract method calls" encapsulates those method calls in an aspect. The aspect contains a pointcut to capture all the places the method should be called and advises that pointcut when to call the refactored method. The most important benefit is the encapsulation of the functionality into the refactoring aspect.

AO refactoring is most useful with crosscutting concerns. In the context of AOP, system-wide crosscutting concerns that span multiple classes and packages receive the most attention. However, there are important crosscutting concerns that affect a smaller scope, such as those that span just a few methods in a class. Many micro crosscutting concerns such as class-level contract enforcement, lazy initialization of resources, and class-specific exception handling are suitable for AO refactoring. You can also apply AO refactoring in situations where system-wide concerns are applied restrictively to only a few classes. Often, refactoring aspects that start with a restrictive approach grow into widely applicable aspects.

While the most visible benefit of AO refactoring comes when there are multiple crosscutting locations, it is useful even in single location cases to encapsulate non-core functionalities. This situation is like one in conventional refactoring. For example, when you extract a method even when there is only one place making a call to that method, you gain improved understanding of the code. Over time, you find that multiple places require a call to the extracted method. Similarly, in AO refactoring, you will realize that the join points that need the functionality keep expanding. In any case, the refactoring aspect localizes crosscutting functionalities such as contract enforcements and access checks from the core implementation.

The relationship between refactoring and AOP goes much deeper. First, developers can introduce aspects more easily into systems where program elements (classes, methods, etc.) carry a simple and well-defined responsibility. Theoretically, systems with such characteristics may be created during the initial implementation, while practically, they often result from refactoring efforts. Second, many conventional and additional refactoring techniques (such as extract pointcut and extract base aspect) can be applied to aspects, too. However, neither of these situations is the focus of this article.

Since AO refactoring really consists of an additional set of refactoring techniques, refactoring principles such as making changes in small steps, using test cases, and running them before and after making changes, continue to apply exactly as prescribed.


Peculiarities of AO refactoring

While most commonly known AOP principles continue to apply to AO refactoring, a few of those principles acquire a particular importance or a different perspective in AO refactoring. Let's consider those principles:

Approach towards crosscutting functionality: In AOP discussions, the focus is often on adding a new crosscutting functionality to an existing application. In practice, the recommended process is to first design and even prototype a conventional solution. Then you can design and implement aspects to encapsulate that functionality. The refactoring approach is similar. Since you already have a working conventional solution, AO refactoring helps you encapsulate the implemented functionality.

Applicability of aspect's crosscutting: When implementing a crosscutting functionality using aspects, a pragmatic approach calls for restricting crosscutting to only selected parts of the system. This way you limit the effect of those aspects. Then you may slowly increase the scope of the aspects, sometimes leading to a unrestricted system-wide application. Refactoring usage puts extra emphasis on this approach. Refactoring aspects deliberately limit crosscutting to typically just a few methods or a class, and sometimes a package. The lexical-based pointcuts such as within() and withincode() are very handy to impose the desired restrictions. While AO refactoring techniques do start with a very restrictive approach, they often have potential for a wider application.

Coupling consideration: In all AOP usage, it is desirable to minimize coupling between aspects and classes. In refactoring usage, however, the coupling issue receives less emphasis. A refactoring aspect is a part of the target class' implementation and therefore it may use intimate knowledge (such as specific variable names or an explicit list of methods) from the class. Of course, if there is a way to minimize coupling, it is always a desirable thing.

Placement of aspects: In refactoring usage, since the aspects may have to change with the implementation, it is desirable to put aspects closer to the target module. For example, if the refactoring target is a class, you could implement the refactoring aspects in the same source file, and even as nested aspects. While all AOP usage often employs nested aspects; in refactoring usage, they gain an additional emphasis.

In summary, AO refactoring builds on common AOP principles and practices, with a change of emphasis on a few. As you would expect, with either kinds of usage, AOP continues to offer benefits such as better modularization, improved comprehensibility, and enhanced maintainability.


A case in point: Extract method calls refactoring

We will examine the aspect-oriented refactoring process through an example of one of the simplest techniques of "Extract method calls". We will use a simple class with a few methods as the refactoring target to allow us to focus on the process. This example shows how to effectively use currently available tools to achieve AO refactoring until IDEs provide automated support, the way they do for conventional refactoring.

Bear in mind that, due to the simple example and refactoring technique used, some of the steps may look like overkill, but they are very useful on real systems. Due to the same reasons, you will not see much saving in code. The second part of the series will show many techniques such as "Extract exception handling" that offer substantial code saving. The goal of this part is to introduce the AO refactoring flavor and process.

Let's consider the implementation of the Account class in Listing 1 that performs a permission check at the beginning of most methods. We will take this code as the starting point and go through a few iterations to arrive at well-refactored code.

Listing 1: Account.java before applying any aspect-oriented refactoring

package banking;

 

import java.security.AccessController;

 

public class Account {

    private int _accountNumber;

    private float _balance;

 

    public Account(int accountNumber) {

        _accountNumber = accountNumber;

    }

 

    public int getAccountNumber() {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        return _accountNumber;

    }

 

    public void credit(float amount) {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        _balance = _balance + amount;

    }

 

    public void debit(float amount) throws InsufficientBalanceException {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        if (_balance < amount) {

            throw new InsufficientBalanceException("Insufficient total balance");

        } else {

            _balance = _balance - amount;

        }

    }

 

    public float getBalance() {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        return _balance;

    }

 

    public String toString() {

        return "Account: " + _accountNumber;

    }

}

As you can see, there isn't much left for a conventional technique to refactor the permission checks; a method call to checkPermission() (bold in the listing) is spread into almost every method.

For the "Extract method call" refactoring, we will break our refactoring process into two required steps followed by two optional steps. For other kinds of refactoring, the steps will vary a little, but the style will remain the same. At the end of each step, the behavior would match that of the original code, and therefore, you should run your unit tests to ensure that refactoring did not change any behavior - after all, it is salient to any refactoring effort.


Step 1: Introduce a no-op refactoring aspect

The purpose of this step is to create the required infrastructure (static and dynamic crosscutting), but add no crosscutting functionality. In essence, we are limited to declare error and declare warning and advice without additional code to execute.

The first step can be broken into three sub steps:

Insert an empty aspect: We insert an empty nested aspect that will eventually implement a permission check for the Account class. Note that the use of a nested aspect (or a peer aspect in the same source file) simplifies tracking changes in the refactored code. The aspect looks like:


private static aspect PermissionCheckAspect {

}


Define a pointcut to capture join points that need the refactored functionality: Here we create a pointcut that captures all the join points where we would like to add the refactored functionality. To minimize unwanted effects, the pointcut simply enumerates each of the required methods. The pointcut definition looks at the following:


private pointcut permissionCheckedExecution() :

    (execution(public int Account.getAccountNumber())

     || execution(public void Account.credit(float))

     || execution(public void Account.debit(float)

                         throws InsufficientBalanceException)

     || execution(public float Account.getBalance()))

    && within(Account);

While we used a fully-specified individual pointcut, you may choose to omit some parts (such as exceptions declared by each method) or replace part with wildcards (such as using ".." instead of specifying each argument). The use of within() ensures that the pointcut does not capture any join points outside the Account class. In our example, the use of within() is redundant and only serves to emphasize the scope of crosscutting.

Create no-op advice to the pointcut: Depending upon the needed position of crosscutting logic, use an appropriate form of advice (before, after, after returning, after throwing, or around). Note that if the crosscutting requires code to execute at multiple positions with respect to the join points, we will need more than one advice, for example, a before advice and an after advice. If you use before or after advice, leave the body empty and in case of around advice, just add a proceed() statement inside the body.

Since in our case, we need to execute the access check before execution of business logic, a before advice is the appropriate choice.


before() : permissionCheckedExecution() {

}


Here is the source code that we have after implementing this step:

Listing 2: Account.java after completing the first step


package banking;

 

import java.security.AccessController;

 

public class Account {

    private int _accountNumber;

    private float _balance;

 

    public Account(int accountNumber) {

        _accountNumber = accountNumber;

    }

 

    public int getAccountNumber() {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        return _accountNumber;

    }

 

    public void credit(float amount) {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        _balance = _balance + amount;

    }

 

    public void debit(float amount) throws InsufficientBalanceException {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        if (_balance < amount) {

            throw new InsufficientBalanceException("Insufficient total balance");

        } else {

            _balance = _balance - amount;

        }

    }

 

    public float getBalance() {

        AccessController.checkPermission(

            new BankingPermission("accountOperation"));

        return _balance;

    }

 

    public String toString() {

        return "Account: " + _accountNumber;

    }

 

    private static aspect PermissionCheckAspect {

        private pointcut permissionCheckedExecution() :

            (execution(public int Account.getAccountNumber())

             || execution(public void Account.credit(float))

             || execution(public void Account.debit(float)

                                 throws InsufficientBalanceException)

             || execution(public float Account.getBalance()))

            && within(Account);

 

        before() : permissionCheckedExecution() {

        }

    }

}

Now let's examine if we captured all the required join points correctly. We can utilize AspectJ-integrated IDE's support to make this easier through visual inspection. Figure 2 shows the use of an outline view in Eclipse.



(Click to see full scale image)

Figure 2: Examining crosscut points through IDE. The outline view and gutter annotations show interactions between aspects and classes.


As figure 2 demonstrates, you can see all the places where the advice is applied. You could click on each of the advised methods and see if indeed that site has the refactored method call. So far, we have left the functionality unchanged.


Step 2: Introduce the crosscutting functionality

In this step, we finally move code from the core class into the aspect. We also perform some additional work to assist in a correct implementation and remind us of the refactoring aspect in the future.

Introduce compile-time warnings (Optional): This recommended step utilizes AspectJ's facility of user-specifiable compile-time warnings to express our intention that only the refactoring aspect - and not the class - shall make the required calls. Further, such a construct will issue warnings in case a programmer, unaware of the aspect, introduces the refactored method back into the class.


declare warning:

    call(void AccessController.checkPermission(java.security.Permission))

    && within(Account)

    && !within(PermissionCheckAspect)

    : "Do not call AccessController.checkPermission(..) from Account";

Note that you will want to leave in the declare warning statement to warn a new developer that there should not be a checkPermission() method anywhere in the class.

Add crosscutting functionality to advice: Now we add the require method calls into the advice. In our case, we will add calls to AccessController.checkPermission().


before() : permissionCheckedExecution() {

    AccessController.checkPermission(

        new BankingPermission("accountOperation"));

}

Remove method calls from the advised methods: Since advice performs the permission check, we need to remove the calls from each of the advised methods. Therefore, this is time to eliminate those checks from each of the methods. You can use IDE's outline view to find each place captured by the pointcut and simply remove the calls from there. If you miss a method, the declare warning statement will issue a warning as shown in figure 3.



(Click to see full scale image)

Figure 3: Utilizing declare warnings to catch missed methods. Note the gutter annotation besides the call to checkPermission() method.


In figure 3, we see how our declare warning caught a call that we failed to remove.

In a larger system, you might want to write an around advice to make these method calls a no-op and test the result before actually removing the code. For example, in our system, you can introduce the following advice to nullify calls to the AccessController.checkPermission() method from the Account class. Notice that the pointcut used here is the same as that used in the declare warning statement discussed. Once you successfully complete the testing, you may remove the advice.


void around() : call(void AccessController.checkPermission(java.security.Permission))

                && within(Account) && !within(PermissionCheckAspect) {

    // do NOT call proceed()

}

Listing 3 shows how the code looks at this point.

Listing 3: Account.java after completing the second step


package banking;

 

import java.security.AccessController;

 

public class Account {

    private int _accountNumber;

    private float _balance;

 

    public Account(int accountNumber) {

        _accountNumber = accountNumber;

    }

 

    public int getAccountNumber() {

        return _accountNumber;

    }

 

    public void credit(float amount) {

        _balance = _balance + amount;

    }

 

    public void debit(float amount) throws InsufficientBalanceException {

        if (_balance < amount) {

            throw new InsufficientBalanceException("Insufficient total balance");

        } else {

            _balance = _balance - amount;

        }

    }

 

    public float getBalance() {

        return _balance;

    }

 

    public String toString() {

        return "Account: " + _accountNumber;

    }

 

    private static aspect PermissionCheckAspect {

        // optional

        declare warning:

            call(void AccessController.checkPermission(java.security.Permission))

            && within(Account)

            && !within(PermissionCheckAspect)

            : "Do not call AccessController.checkPermission(..) from Account";

        // end optional

           

        private pointcut permissionCheckedExecution() :

            (execution(public int Account.getAccountNumber())

             || execution(public void Account.credit(float))

             || execution(public void Account.debit(float)

                                 throws InsufficientBalanceException)

             || execution(public float Account.getBalance()))

            && within(Account);

 

        before() : permissionCheckedExecution() {

            AccessController.checkPermission(

                new BankingPermission("accountOperation"));

        }

    }

}

We now have all the permission checks removed from the core and encapsulated in a separate aspect. You could stop here. However, you should consider two more optional steps. While the first optional step will help if the code for the refactored class evolves in a certain way, the second step will help to extract reusable parts of the refactoring aspect.


Step 3 (Optional): Simplify pointcut definition

In this optional step, we redefine the pointcut to make it shorter and make it semantically more meaningful. Note that in step 2, the pointcut defined will capture just the enumerated methods with an exact signature match. This helped us to guarantee preservation of original behavior for now. However, if methods that need the same functionality are added later, doing so will require modifying the pointcut definition. Further, we would also like to guard against changes in method signatures because they would stop the advice from being applied to the changed methods. In this step, we specify an alternative definition for the pointcut to avoid this problem.

Note that this step isn't a mechanical step and requires understanding of the interaction with the functionality and due diligence in implementation. Since you may arrive at an equivalent pointcut in many ways, it is important that you come up with a pointcut expression that semantically ties all the required join points. On most occasions, it is a good idea to capture a pointcut that captures a wide set of join points based on semantics and then account for exceptional join points. For example, you may capture all public methods if the functionality applies to all externally available methods or all methods with name starting with "set" for all state-modifying methods. If there are exceptions, you may augment the pointcut with exceptional cases. This way any modification in your code such as addition or renaming of methods would more likely yield the correct functionality.

In the Account class, we observe that all public methods except the toString() method call the AccessController.checkPermission() method. Let's capture this observation by modifying the pointcut definition. At this point, you may want to utilize IDE's crosscutting view to see if you captured the right methods. Note that the manual inspection may get tedious and error-prone when the pointcut captures a large number of join points. See the sidebar at the end of this part for an alternative technique.


private pointcut permissionCheckedExecution()

    : (execution(public * Account.*(..))

       && !execution(String Account.toString()))

      && within(Account);

Listing 4 shows the Account class after all the above changes.

Listing 4: Account.java after completing the third step


package banking;

 

import java.security.AccessController;

 

public class Account {

    private int _accountNumber;

    private float _balance;

 

    public Account(int accountNumber) {

        _accountNumber = accountNumber;

    }

 

    public int getAccountNumber() {

        return _accountNumber;

    }

 

    public void credit(float amount) {

        _balance = _balance + amount;

    }

 

    public void debit(float amount) throws InsufficientBalanceException {

        if (_balance < amount) {

            throw new InsufficientBalanceException("Insufficient total balance");

        } else {

            _balance = _balance - amount;

        }

    }

 

    public float getBalance() {

        return _balance;

    }

 

    public String toString() {

        return "Account: " + _accountNumber;

    }

 

    private static aspect PermissionCheckAspect {

        // optional

        declare warning:

            call(void AccessController.checkPermission(java.security.Permission))

            && within(Account)

            && !within(PermissionCheckAspect)

            : "Do not call AccessController.checkPermission(..) from Account";

        // end optional

 

        private pointcut permissionCheckedExecution()

            : (execution(public * Account.*(..))

               && !execution(String Account.toString()))

              && within(Account);

           

        before() : permissionCheckedExecution() {

            AccessController.checkPermission(

                new BankingPermission("accountOperation"));

        }

    }

}


Given the kind of changes we made, this is an especially good time to run your unit tests.


Step 4 (Optional): Refactor the refactoring aspect

In this step, we may refactor the refactoring aspect itself. Following the agile process wisdom, you will probably want to wait until you see some other class needing the same refactoring. The process typically involves creating an abstract aspect and moving most of the functionality to it. The base aspect contains a few abstract pointcuts and methods. The concrete refactoring aspects for each refactored module extends this aspect and provides definition for pointcuts and implementation for the methods. In a sense, such a refactoring is the AOP equivalent of "Extract superclass" refactoring, called "Extract base aspect".


Preview of the second part

In this article, we examined the general approach to AO refactoring with a concrete example. We also examined how the current level of IDE integration helps in the refactoring steps. The example chosen was quite simple and thus allowed us to walk through the process without being bogged down in either the core logic or AspectJ syntax.

However, we only scratched the surface as far as the techniques available. In the second part, we will look at several techniques, such as extract exception handling, extract concurrency control, replace argument trickle by wormhole, extract worker object creation, extract lazy initialization, extract interface implementation, and replace override with advice. We will take examples of common Java and J2EE patterns and refactor them to achieve the common benefits of AOP - clarity of intentions, brevity of code, ease of modifications, and so on. Stay tuned.


Utilizing static crosscutting to flag mismatched join points

While in simple cases (such as the Account class), you could create a pointcut with a shorter definition directly, for more complex cases, there is a better technique that helps to avoid any mismatch between the original pointcut definition and its newer version. In this technique, we create a temporary pointcut that captures the join points according to their semantics. In our Account class example, it "seems" that all public methods need permission check. Therefore, we will define the temporary pointcut to capture all public methods of the Account class. This pointcut will eventually replace the definition for the permissionCheckedExecution() pointcut.


pointcut temp() :  execution(public * Account.*(..)) && within(Account);


Now we will use an idiom that produces errors if two pointcuts do not match exactly the same set of join points. Note that this idiom is possible only when both pointcuts are statically determinable, which means they must not use this(), target(), args(), cflow(), cflowbelow(), or if() pointcuts. Often, you are able to refactor a pointcut itself to separate the statically determinable parts from the rest, allowing the use of the presented idiom. The statement that implements this idiom for our example is as follows:


declare error:

    (!permissionCheckedExecution() && temp())

    || (permissionCheckedExecution() && !temp())

    : "Mismatch in join points captured";


Any errors issued by compiler imply that the two pointcuts aren't equivalent. The way we have set it up, we will get an error since temp() captures the toString() method but permissionCheckedExecution() does not.



(Click to see full scale image)

Figure 4: Capturing mismatched join points using a declare error construct.


As seen in figure 4, the error points to the toString() method. Let's repair that by eliminating that method from the captured join points. Now compiler will not produce any errors.


pointcut temp()

    : (execution(public * Account.*(..))

       && !execution(String Account.toString()))

      && within(Account);

Note that some of these errors may reveal bugs in the original code such as incorrectly skipped method calls from a few methods. Inadequate unit testing may have kept those bugs latent. If such is the case, you may utilize this opportunity to fix those bugs and update the unit tests.

Now that we have a shorter equivalent pointcut, just change its name to the original pointcut, and remove the original pointcut.


private pointcut permissionCheckedExecution()

    : (execution(public * Account.*(..))

       && !execution(String Account.toString()))

      && within(Account);

This is the same pointcut definition that we created directly using mere observation, as described in the third step.



Acknowledgements

Thanks to Ron Bodkin, Mik Kersten, and Gregor Kiczales for reviewing the manuscript and providing useful feedback.


About the Author

Ramnivas Laddad is the author of several articles, papers, and books. His most recent book, "AspectJ in Action: Practical aspect-oriented programming" (Manning, 2003), has been labeled as the most useful guide to AOP/AspectJ. Ramnivas has been developing complex software systems using technologies such as Java, J2EE, AspectJ, UML, networking, real-time systems, and XML for over a decade. He is an active member of the AspectJ user community and has been involved with aspect-oriented programming from its early form. He is also a mentor at AspectMentor, a consortium of AOP experts who provide assistance with training, consulting, and mentoring. You can reach him at ramnivas@yahoo.com


References

  • Understand conventional refactoring techniques:
    Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts, Refactoring: Improving the Design of Existing Code, Addison Wesley, 1999.

  • Know all about AOP and AspectJ, with many examples ranging from simple logging and policy enforcement to authorization and transaction management. The examples presented in this book should provide ideas for AO refactoring:
    Ramnivas Laddad, AspectJ in Action: Practical Aspect-Oriented Programming, Manning, 2003.

    TheServerSide.com features two sample chapters from "AspectJ in Action": /articles/AspectJreview.tss

  • Find more about the fundamental of AOP and AspectJ (based on AspectJ 1.0, but most information is still applicable):
    Ramnivas Laddad, I want my AOP (part 1-3), JavaWorld, January 2002.

  • Read more about the AspectJ programming language:
    Joseph D. Gradecki, Nicholas Lesiecki, Mastering AspectJ: Aspect-Oriented Programming in Java, John Wiley & Sons, 2003.

  • Understand whys and hows of AOP:
    Gregor Kiczales, Crosscut (monthly column), Software Development magazine.

  • Download AspectJ tools, documentation, and links to plugins for various IDEs:
    http://www.eclipse.org/aspectj

  • Download tool that helps to find and manage crosscutting concerns, Aspect Browser:
    http://www.cs.ucsd.edu/users/wgg/Software/AB

  • Download Eclipse plugin that helps to explore crosscutting concerns in an existing system, Feature Exploration and Analysis Tool (FEAT):
    http://www.cs.ubc.ca/labs/spl/projects/feat

  • Download tool to mine aspects in an existing system, Aspect Mining Tool (AMT):
    http://www.cs.ubc.ca/~jan/amt

  • A tool in making that will support Dialogue-Based Aspect-Oriented Refactoring:
    http://www.cs.ubc.ca/labs/spl/projects/ao-refactoring.html