I thought I'd share what I've done to accomplish what I needed. I have
solved the problem a couple of different ways.
1.) Interceptor for Transfer transactions
Since Transfer's AOP was giving me nightmares, I wrote a quick
interceptor with this method:
<cffunction name="adviseAction" access="public">
<cfargument name="Event" required="true"
type="coldbox.system.beans.requestContext" hint="The event object.">
<cfargument name="interceptData" required="true" type="struct" hint="A
structure containing intercepted information. NONE BY DEFAULT HERE">
<cfscript>
transaction = getPlugin("ioc").getBean("TransferTransaction");
</cfscript>
<cfset transaction.execute(interceptData.component,
interceptData.action, arguments)>
<!--- continue interceptin chain --->
<cfreturn false>
</cffunction>
Then in my preHandler() function for the handler who's actions I want
to be able to advise I added this switch:
switch(Event.getCurrentAction())
{
case "adviseTransaction":
{
data = structnew();
data.action = Event.getValue("originalAction");
data.component = this;
announceInterception('adviseAction', data)
break;
}
}
I then added a route for my handler:
<cfset addCourse(pattern="user/:originalAction",
handler="user",
action="adviseTransaction")>
Finally, for the user.adviseTransaction action that the course matches
to I created an empty method that doesn't do anything but is there to
allow the preHandler to run (which executes the original action on the
handler inside of a transaction):
<cffunction name="adviseTransaction" access="public"
returntype="void" output="false">
<cfargument name="Event" type="coldbox.system.beans.requestContext">
<cfset var rc = event.getCollection()>
</cffunction>
So basically what happens here is this:
When the user.create event is called it is routed to
user.adviseTransaction with a variable available called
"originalAction" which contains the real action that we want to
execute inside of a transaction. In this case, "create".
When user.adviseTransaction runs, the preHandler first matches to the
case and announces an interception to the adviseAction() method of the
interceptor, passing in a structure containing the original action as
data.action and the user handler reference as data.component
The interceptor then retrieves the transaction manager for Transfer
and executes the original method ("create") by using the
interceptData.action variable to identify the action and the
data.component varible to reference the handler. The views, etc are
set inside of the create method since it has access to the Event, the
adviseTransaction method on the handler doesn't need to do anything.
This executes the create method on the user component inside of a
transaction without having to user runEvent(). The Event argument to
the interceptor's adviseAction() method is passed through to
user.create during the execute call.
This effectively wraps an entire handler method inside of a
transaction, so that in case you want to call multiple service layers
and have all of them wrapped under one transaction, you are free to do
so by wrapping the entire handler action.
One thing to remember is that when I have a route such as:
<cfset addCourse(pattern="user/:originalAction",
handler="user",
action="adviseTransaction",
packageResolverExempt="false")>
This sends user.[anything] to user.adviseTransaction, obviously I
don't want this. My user.viewUser has no need to be inside of a
transaction.
So I can do this:
<cfset addCourse(pattern="user/create",
handler="user",
action="adviseTransaction",
packageResolverExempt="false",
matchVariables="originalAction=create")>
This will send only the user/create event to the adviseTransaction()
method.
On alternative way of doing all of this is to avoid the interceptor
entirely and put the method inside of the user handler, like so:
<cffunction name="adviseAction" access="public" returntype="void"
output="false">
<cfargument name="Event" type="coldbox.system.beans.requestContext">
<cfset var rc = event.getCollection()>
<cfscript>
transaction = getPlugin("ioc").getBean("TransferTransaction");
</cfscript>
<cfset transaction.execute(this, rc.originalAction, arguments)>
</cffunction>
And then tell the route to do:
<cfset addCourse(pattern="user/create",
handler="user",
action="adviseAction",
packageResolverExempt="false",
matchVariables="originalAction=create")>
With this configuration, you don't need anything in the preHandler()
method or have an interceptor. The route will pass user/create to
user.adviseAction, which in turn executes the originalAction from the
request collection on "this" (the current handler) inside of a
transaction.
I prefer the handler because it is more flexible, but either way, it
works :)
I can use AOP for singletons, such as service calls and I can wrap my
entire handler action in a transaction using this method.
This has eliminated my stack overflow issues.
On Jun 28, 11:53 pm, Mark Mandel <
mark.man...@gmail.com> wrote:
> It * sounds * like Transfer is being loaded on every request... I would put
> some <cflog> statements in TransferFactory's init() function, and see if
> that is the case.
>
> Mark
>