Copyright © 2011 Alexei Krasnopolski
Version: 1.0.1
Introduced in: 2011-07-15
Authors: Alexei Krasnopolski (krasnop@bellsouth.net) [web site: http://crasnopolski.com].
References
See also: aop.
Before starting an explanation of using the tool, lets refresh memory about generic AOP terms follow Aspect Oriented Programming with Spring. Not all principal of AOP are implemented in the project so we will discuss only following ones. Definitions are slightly changed to fit Erlang reality.
Figure below illustrates the discussed terms.
erl.aop | |== ebin |== examples | |== aspects | | |---- advices.erl | | |---- defs.adf | | | |== target | | |---- program.erl | | | |---------- run_program.erl | |== include | |---------- aop.hrl | |== src |---------- aop.erl |---------- fun_proxy.erl |---------- weaver.erlBefore starting with example we need to prepare the project. First let's open Erlang console (eshell) and change current dir to the project ebin folder:
1> c:cd("/home/alexei/eclipse/data/workspace/erl.aop/ebin"). /home/alexei/eclipse/data/workspace/erl.aop/ebinNext step is compiling source files from src folder:
2> c("../src/aop.erl"). {ok,aop} 3> c("../src/fun_proxy.erl"). {ok,fun_proxy} 4> c("../src/weaver.erl"). {ok,weaver}Now we can go to accomplish a few following steps to make AOP works.
-module(program). -export([calc_service/2, process_message/1, run/0]). calc_service(A, B) -> A + B. process_message(M) -> string:to_upper(M). run() -> calc_service(1, 0), calc_service(2, 2), process_message("Add 3 to 5"), process_message("Message to process"), ok .
-module(advices). -export([before_advice/1, after_advice/2]). before_advice([M, F, Args]) -> io:format(">>> before function ~p:~p~n arguments:~n", [M, F]), [io:format(" ~p~n", [Arg])|| Arg <- Args] . after_advice([M, F, _Args], R) -> io:format("<<< after function ~p:~p~n return:~p~n", [M, F, R]) .
%% defs.adf [Aspect( Advice(before, advices, before_advice), [ Pointcut("program", "\\w*_service", "*", public) ] ), Aspect( Advice(after_return, advices, after_advice), [ Pointcut("program", "process\\w*", "1", public) ] )] .
2> aop:compile(["../examples/target"], ["../examples/aspects"]). Sources = ["../examples/program.erl"] Config files = ["../examples/aspects/defs.adf"] Module name = program is weaved. Warnings = [] ok
3> program:run(). >>> before function program:calc_service_@ arguments: 1 0 >>> before function program:calc_service_@ arguments: 2 2 <<< after function program:process_message_@ return:"ADD 3 TO 5" <<< after function program:process_message_@ return:"MESSAGE TO PROCESS" ok
---------- -------------------------- | defs.adf |----->----| weaver:parse_transform/2 | ---------- -------------------------- | | --- parse_transform | ---------------------- ----------------- -------------------- | original source code |---->----| ERLANG Compiler |---->----| AOP adviced source | ---------------------- ----------------- --------------------Table below describes how weaver transforms original function to proxy function with embedded calling of advice functions.
Target function (original source code) |
Target function after weaving |
---|---|
target_func(P1,P2) -> {body}. |
target_func_@(P1,P2) -> {body}. |
Advice type | AOP proxy function after weaving |
before | target_func(P1,P2) -> before_advice([M,F,[P1,P2]]), target_func_@(P1,P2). |
after return | target_func(P1,P2) -> R = target_func_@(P1,P2), after_advice([M,F,[P1,P2]], R), R. |
after throw | target_func(P1,P2) -> try target_func_@(P1,P2) catch Ex:Rz -> after_advice([M,F,[P1,P2]],{Ex,Rz}), end. |
after final | target_func(P1,P2) -> try target_func_@(P1,P2) after after_advice([M,F,[P1,P2]]), end. |
around | target_func(P1,P2) -> erlang:apply(advice_module, arround_advice, [?MODULE, target_func_@, [P1,P2]]). |
Advice functions have to obey some rules as explained below.
Advice type | Function specification |
---|---|
before | @spec before_advice([M, F, Args]) -> any() M = atom() - target module name F = atom() - target function name Args = list() - list of arguments of target function |
after return | @spec after_advice([M, F, Args], R) -> any() M = atom() - target module name F = atom() - target function name Args = list() - list of arguments of target function R = any() - return value of target function |
after throw | @spec after_advice([M, F, Args],{Ex,Rz}) -> any() M = atom() - target module name F = atom() - target function name Args = list() - list of arguments of target function Ex = term() - exception that is caught during target function execution. Rz = term() - reason of the exception |
after final | @spec after_advice([M, F, Args]) -> any() M = atom() - target module name F = atom() - target function name Args = list() - list of arguments of target function |
around | @spec around_advice(M, F, Args) -> R M = atom() - target module name F = atom() - target function name Args = list() - list of arguments of target function R = any() - return value of target function The body of around_advice have to contain at least - erlang:apply(M, F, Args) Around advice has to return a value that returns the call above. |
Configuration element (function) |
Arguments |
---|---|
Aspect(Advice, Pointcut) | Advice - tuple that defines the advice; Pointcut - list that defines list of pointcuts for the advice. |
Advice(Module, Function) | Module = atom() - name of advice module; Function = atom() - name of advice function. |
Pointcut(Module, Function, Arity, Scope) | Module = string() - regular expression that matches module names of desired joint points, ex.: "connection" - defines joint point module 'connection', "\\w*_db" - defines all joint point modules with suffix '_db'; Function = string() - regular expression that matches function names of desired joint points; ex.: "calc" - defines joint point function 'calc', "\\w*_service" - defines all joint point functions with suffix '_service'; Arity = string() | integer() - expression that matches arity of joint point functions, ex.: 1 - defines arity of joint point function equals 1, * - defines any arity, "2-4" - defines arity >= 2 and <= 4. Scope = public | local | global - match for exported function (public), private (local) function or both (global). |
Generated by EDoc, Aug 3 2012, 20:09:54.