A Development-Time Aspect
Listings 1–3 show an example of a development-time aspect. This would be very useful for getting some trace details from existing software (hence the aspect name Tracing in the file Tracing.aj). To illustrate and expand on this point, let’s add some code to the HelloWorld.java file as shown in Listing 4.
Listing 4 A New Method and a Data Member Added to HelloWorld.java
public class HelloWorld { public static void setanIntVar(int newValue) { System.out.println("Value passed to setanIntVar() " + newValue); anIntVar = newValue; } public static int getanIntVar() { return anIntVar; } public static void anotherMethod() { System.out.println("Hello World from anotherMethod()"); //We’ll now change the value of anIntVar setanIntVar(anIntVar + 1); } public static void main (String[] args) { System.out.println("Hello World from main()!"); setanIntVar(500); anotherMethod(); } static int anIntVar; }
I’ve added a data member called anIntVar and three additional methods:
- setanIntVar()
- getanIntVar()
- anotherMethod()
We’re basically bulking up HelloWorld.java to give an idea of how AOP can be used in a development environment. I use setanIntVar() to set the value of anIntVar to 500 (no big surprise there). Next, I call anotherMethod() at the end of main() to call setanIntVar().
Following the earlier example, I now create a new aspect in the file Tracing2.aj in the same folder as HelloWorld. This new aspect is shown in Listing 5.
Listing 5 A New Aspect for Debugging
public aspect Tracing2 { private pointcut setanIntVar () : execution(static void setanIntVar(int)); before () : setanIntVar() { System.out.println("> Before setanIntVar() value is " + HelloWorld.getanIntVar()); } after () : setanIntVar() { System.out.println("< After setanIntVar() value is " + HelloWorld.getanIntVar()); } }
The new aspect intercepts calls to setanIntVar() just before and just after it executes. This granularity allows me to display the value of the data member in question by calling getanIntVar(). So we’ll be able to trace changes to the value of anIntVar.
Now we have to build the new aspect along with the modified HelloWorld.java. To do this, just run the commands shown in Listing 6.
Listing 6 Output from the New Aspect
ajc -outjar hello.jar HelloWorld.java ajc -outjar tracing2.jar -outxml Tracing2.aj set classpath=%classpath%;.\tracing2.jar aj HelloWorld > Before mainMethod()execution(void HelloWorld.main(String[])) Hello World from main()! > Before setanIntVar() value is 0 Value passed to setanIntVar() is 500 < After setanIntVar() value is 500 Hello World from anotherMethod() > Before setanIntVar() value is 500 Value passed to setanIntVar() is 501 < After setanIntVar() value is 501 < After mainMethod()execution(void HelloWorld.main(String[]))
All the calls to setanIntVar() are intercepted in Listing 6. The value of anIntVar changes from 0 to 500 and finishes at 501. In the last four lines of Listing 6, the value of anIntVar changes from 500 to 501. In other words, we’ve traced all the changes to this data member.
On a more general level, we can use such aspects to trace the progress of both code execution and data changes. The real value of this capability is that all the trace code complexity lies in the aspect code, without affecting the application code. This feature avoids the problem of changing application code in order to extract runtime information. And it is a problem changing application code!
One software product I worked on for a telecom company had no debugging facilities apart from calls to println(). I remember one situation when, after entering a lot of trace code, I finally fixed a bug. When I removed the (now unnecessary) trace code, I inadvertently commented a line of application code. The algorithm broke (again) and I had to fix the new bug authored by yours truly! If the trace code had been separate, I wouldn’t have been able to break the application code.
Let’s take a look at a production-time aspect—an aspect that you might leave in the finished product.