Wiring the Coldbox Logger Plugin To An AOP Logging Advice

2009 January 13
tags: AOP · Coldbox · ColdFusion · ColdSpring
by Paul Marcotte

Logging is one of the classic cross-cutting concerns one hears about when discussing Aspect Oriented Programming (AOP). Other common aspects are caching, security and data transformation. Over the past year, I've begun to apply AOP with ColdSpring to my application design. I was completely confused about just what was going on with AOP, when I first looked at it, then, by degrees, I came to realize that it is actually pretty simple if you can focus on the basics without trying to absorb all the terms associated with AOP. With the release of ColdSpring 1.2, Brian Kotek wrote an excellent quickstart guide, that I recommend (especially for the AOP tutorial which I borrow heavily from for this example). I'll briefly describe how I wrapped my head around the concept.

The three terms that you will want to understand are Advice, Advisor and Proxy.

Advice

I look at an AOP advice as the behaviour I wish to apply. This is often referred to as weaving since the behaviour is woven into your code through AOP. Therefore, if I want to apply logging to a component, I create a "LoggingAdvice". An advice component extend one of several base advice type found in the coldspring.aop package. There are many advice types, but I will discuss only the MethodInterceptor advice.

Here is my LoggingAdvice.

<cfcomponent output="false" displayname="LoggingAroundAdvice" hint="I advise service layer methods and apply logging." extends="coldspring.aop.MethodInterceptor">

<cffunction name="init" returntype="any" output="false" access="public" hint="I return the LoggingAroundAdvice.">
<cfreturn this />
</cffunction>

<cffunction name="invokeMethod" returntype="any" access="public" output="false" hint="I log the method name and arguments.">
<cfargument name="methodInvocation" type="coldspring.aop.MethodInvocation" required="true" hint="" />

<!--- Capture the arguments and method name being invoked. --->
<cfset getLogger().info("method: " & arguments.methodInvocation.getMethod().getMethodName()
& " arguments: " & SerializeJSON(StructCopy(arguments.methodInvocation.getArguments()))
)>


<!--- Return the result of the method call. --->
<cfreturn arguments.methodInvocation.proceed() >
</cffunction>

<cffunction name="setLogger" access="public" returntype="void" output="false" hint="I set a reference to the Logger.">
<cfargument name="Logger" type="any" required="true">
<cfset variables.Logger = arguments.Logger >
</cffunction>

<cffunction name="getLogger" access="public" returntype="any" output="false" hint="I return the Logger.">
<cfreturn variables.Logger />
</cffunction>

</cfcomponent>

And the bean definition for the advice.

<bean id="loggingAdvice" class="model.aspects.LoggingAroundAdvice">
<property name="logger">
<ref bean="logger" />
</property>
</bean>

Advisor

I like to think of an Advisor as the strategy for applying an advice. The what and how. Typically, you'll supply the advisor with two arguments. The advice and how you want the advice applied (which can be aregex pattern, a list of mapped names, or a "Pointcut"). Mapped names, patterns, or pointcuts all apply to the methods within a targeted component. Which leads into the last term.

Here is my Advisor bean definition.

<bean id="loggingAdvisor" class="coldspring.aop.support.NamedMethodPointcutAdvisor">
<property name="advice">
<ref bean="loggingAdvice" />
</property>
<property name="mappedNames">
<value>*</value>
</property>
</bean>

Proxy

In order to apply the advice to your component, ColdSpring creates a proxy. You treat the proxy as you would the original target component since it will maintain the same public api as the target component, only with additional behaviour applied. There are a couple of ways to create a proxy, here is the way I choose to do it using an "anonymous" inner bean.

<bean id="SecurityService" class="coldspring.aop.framework.ProxyFactoryBean">
<property name="target">
<bean factory-bean="ServiceFactory" factory-method="getService">
<constructor-arg name="packageName"><value>security</value></constructor-arg>
<property name="MessagingService"><ref bean="MessagingService" /></property>
</bean>
</property>
<property name="interceptorNames">
<list>
<value>loggingAdvisor</value>
</list>
</property>
</bean>

Gaining Access to the Coldbox Logger Plugin

Thanks to my good buddy, Matt Quackenbush, I learned how to create a ColdSpring bean reference to the ColdBox logger plugin (which is a property of the logging advice above.)

<bean id="ColdboxFactory" class="coldbox.system.extras.ColdboxFactory" autowire="no" />
<bean id="logger" factory-bean="ColdboxFactory" factory-method="getPlugin">
<constructor-arg name="plugin">
<value>logger</value>
</constructor-arg>
</bean>

If you're curious about AOP, but feel overwhelmed by the amount of information you'll need to absorb to understand it completely, it might help to look at it in the simplest way. An Advice defines the aspect you want to apply (logging, caching, or more), the Advisor defines which Advice to apply and how to apply it. And the Proxy defines the target component and Advisor(s).