Tracing Aspect
One of the most simple and well known aspects is the tracing aspect: an aspect that traces the method invocations that occur in the application. First we will see how to implement this using the Reflex standard metaobject protocol (MOP) making use of a special metaobject, and then, how to implement this tracing aspect using the the standard System.out object.
Using the Standard MOP
As in every Reflex configuration we need to define the hookset(s), the metaobject definition(s) and the behavioral link(s). First, we need a hookset that matches all method receptions in our application:
Hookset theHookset = new PrimitiveHookset( MsgReceive.class, new NameCS("reflex.examples.fibonacci.app.Fibonacci"), new DeclaredInOS("reflex.examples.fibonacci.app.Fibonacci") );
Here we use two utility subclasses of ClassSelector and OperationSelector:
- NameCS: selects classes based on the name(s) passed as parameter(s).
- DeclaredInOS: selects operation declared in the class name(s) passed as parameter(s).
Notice that because Reflex acts at loadtime, we always refer to classes using their name and not usingFibonacci.class.getName() because it triggers the loading of the class.
In our case, we are interested in message receives (MsgReceive.class) in the class Fibonacci (new NameCS(“reflex.examples.fibonacci.app.Fibonacci”)) of methods declared in the same class (new DeclaredInOS(“reflex.examples.fibonacci.app.Fibonacci”)).
Second, we need the metaobject definition. In this example, we will use the standard MOP and for this purpose, we will use an utility metaobject: SimpleLogger. This object simply prints every method call giving information about parameters and the returning value.
The more straightforward way to declare a metaobject definition is to use SharedMO, as it takes a pre-existent object as parameter:
MODefinition theMO = new MODefinition.SharedMO(new SimpleLogger());
Finally, we need to declare the behavioral link between the previous two elements:
BLink theLink = Links.get(theHookset, theMO);
That is all the configuration we need. The complete source code of the link provider that adds this link is here. To run the example, just run the following command:
Windows %java -classpath "lib\javassist.jar;build\reflex-core.jar;build\reflex-examples.jar" reflex.examples.tutorial.StdMOPLoggingAspect Linux %java -classpath "lib/javassist.jar:build/reflex-core.jar:build/reflex-examples.jar" reflex.examples.tutorial.StdMOPLoggingAspect
If everything went ok, you should see something like the following output:
Before message receive: Before message receive: main([5]) Before message receive: get([5]) Before message receive: get([4]) Before message receive: get([3]) Before message receive: get([2]) Before message receive: get([1]) After message receive: get is returning: [1] Before message receive: get([0]) ... After message receive: get is returning: [5] The fibonacci[5] is 5 Before message receive: get([6]) Before message receive: get([5]) Before message receive: get([4]) Before message receive: get([3]) Before message receive: get([2]) Before message receive: get([1]) After message receive: get is returning: [1] Before message receive: get([0]) ... After message receive: get is returning: [8] The fibonacci[6] is 8 After message receive: main is returning: [null]
As you can see, the first line corresponds to the main method invocation, then, a lot of recursive calls of the get method are traced. Finally, the exit of the main method is traced (the last line).
In the following section we will see how to implement this without any auxiliary class: direct calls to System.out
will be made with the exact string we want to trace.
Using Call Descriptors and Parameters
In this section we will view how to use a powerful feature of Reflex: call descriptors. Call descriptors allows us to describe the exact method that should be called in the metaobject (ie, the exact MOP we will use), as well as the parameters to use. A detailed review of these elements can be found here.
In this example we will use System.out as our metaobject.
As we want to trace the same operations as before, the hookset remains the same:
Hookset theHookset = new PrimitiveHookset(MsgReceive.class, new NameCS("reflex.examples.fibonacci.app.Fibonacci"), new DeclaredInOS("reflex.examples.fibonacci.app.Fibonacci"));
But, the metaobject definition changes as we are using System.out
directly now:
MODefinition theMO = new MODefinition.SharedMO(System.out);
Here we create a SharedMO with the metaobject we want to use: System.out
(instead of SimpleLogger
).
Then, we create the behavioral link:
BLink theLink = Links.get(theHookset, theMO);
Finally comes the most important part of the configuration, the call descriptor:
theLink.setControl(Control.BEFORE_AFTER); theLink.setCall(Control.BEFORE, "java.io.PrintStream", "println", new BeforeParameter()); theLink.setCall(Control.AFTER, "java.io.PrintStream", "println", new AfterParameter());
Here we are specifing three things:
- First, the control attribute of the link is
BEFORE_AFTER
(detailled information in this page). - Second, for the
BEFORE
control, the println method should be invoked with theBeforeParameter
parameter. - And Finally, for the
AFTER
control, the println method should be invoked too, but this time with theAfterParameter
.
Here is the implementation of BeforeParameter:
private static class BeforeParameter extends StdParameters.StringParameter{ public String evaluate(Operation aOperation){ MsgReceive theMsgReceive = (MsgReceive) aOperation; return ""Before message receive: " + theMsgReceive.getName() + "(" + reflex.core.Tools.getStringArray($args) + ")""; } }
The code above defines a subclass of StdParameters.StringParameter, a base class for parameters which its evaluated type is String. In the evaluate method the evaluation (represented as a String) of the parameter must be returned. As it is a String, the value returned by this method should be, once it has been put in the code, a string (a useful way to figure it out is to print the value returned using System.out, it should look like a String: “Welcome to Reflex” + name + “!”
, including the s
).
Notice that we use a reification of the occurring operation (aOperation) to obtain the name of the method being invoked. At the same time, we use the special variable $args that allows us to access the actual parameters of the method (various special variables are present, see the Javassist tutorial for more information). Also we use reflex.core.Tools.getStringArray
to get a readable representation of the parameters, you could use your own implementation if you like, we use this method for the sake of simplicity only.
The code of the AfterParameter is:
private static class AfterParameter extends StdParameters.StringParameter{ public String evaluate(Operation aOperation){ MsgReceive theMsgReceive = (MsgReceive) aOperation; return ""After message receive: " + theMsgReceive.getName() + " is returning [" + $_ + "] ""; } }
The code above is similar to the one we used to define the BeforeParameter. There is just one difference: we use $_ to access to the return value (here we are using Javassist syntax again). The complete code of the link provider that adds this link to the Reflex configuration is here. To run this example, execute the following command:
Windows %java -classpath "libjavassist.jar;buildreflex-core.jar;buildreflex-examples.jar" reflex.examples.tutorial.CallDescriptorLoggingAspect Linux %java -classpath "lib/javassist.jar:build/reflex-core.jar:build/reflex-examples.jar" reflex.examples.tutorial.CallDescriptorLoggingAspect
If everything went ok, you should see the same output (line by line) as in the previous example:Before message receive:
Before message receive: main([5]) Before message receive: get([5]) Before message receive: get([4]) Before message receive: get([3]) Before message receive: get([2]) Before message receive: get([1]) After message receive: get is returning: [1] Before message receive: get([0]) ... After message receive: get is returning: [5] The fibonacci[5] is 5 Before message receive: get([6]) Before message receive: get([5]) Before message receive: get([4]) Before message receive: get([3]) Before message receive: get([2]) Before message receive: get([1]) After message receive: get is returning: [1] Before message receive: get([0]) ... After message receive: get is returning: [8] The fibonacci[6] is 8 After message receive: main is returning: [null]