-
Notifications
You must be signed in to change notification settings - Fork 24
Using TransferObjects as Event Beans
by Mike Rogers (r.m.rogers@…)
- Introduction
- Two and a Half Solutions
- Solution for Mach-II without ColdSpring
- Solution for Mach-II 1.6 or less using ColdSpring
- Solution for Mach-II 1.8+ using ColdSpring
Transfer is an Object Relational Mapping (ORM) library for ColdFusion. Essentially it's a way for a programmer to interact with the database through an object-oriented interface instead of delving into a relational model. It also generates its own TransferObjects from your configuration file, which cuts down greatly on boring boilerplate code you have to write, which is a plus in my book.
But what if you want to use one of your Transfer objects as an event-bean
? Once you configure Transfer, you have all these wonderful TransferObjects lying around for which you've written no code (just configured them via Transfer's XML config file). Why not pull them into the event
object and use them to hold your form requests, since those form elements are probably going to end up in your Transfer objects anyway?
Well, Mach-II makes this easy-to-trivial, depending on your setup. In this article, we'll explore using TransferObjects as event-beans in a project using various versions of Mach-II. We will also make use of ColdSpring, if it's available.
Since we're not using ColdSpring, we have to make a few assumptions about the layout of the project. First, we assume that the model is the area of the application that most closely works with the creation, update, and deletion of the TransferObjects. Second, we assume that there is a method in the model that creates an empty TransferObject that we can hand to Mach-II so it will populate it with the form input.
Given these assumptions, we need to find a way to put an empty TransferObject into the event
without the aide of ColdSpring. Fortunately Mach-II makes this easy with a simple Filter applied to your event-handler
. Here is the Filter's filterEvent
method:
<cffunction name="filterEvent" access="public" returntype="boolean" output="false"
hint="I am invoked by the Mach II framework.">
<cfargument name="event" type="MachII.framework.Event" required="true"
hint="I am the current event object created by the Mach II framework." />
<cfargument name="eventContext" type="MachII.framework.EventContext" required="true"
hint="I am the current event context object created by the Mach II framework." />
<cfargument name="paramArgs" type="struct" required="false" default="#structNew()#"
hint="I am the structure containing the parameters in mach-ii.xml." />
<cfset var facadeType = paramArgs.model />
<cfset var facadeMethod = paramArgs.facadeMethod />
<cfset var beanName = paramArgs.beanName />
<cfset var coldspring = paramArgs.coldspring />
<cfset var facade = createObject("component", facadeType).init() />
<cfset var facadeBean = ''/>
<cfinvoke component="#facade#" method="#facadeMethod#" returnVariable="facadeBean"/>
<cfset event.setArg(beanName, facadeBean) />
<cfreturn true/>
</cffunction>
And here is the configuration for an event-handler
:
<event-handler event="login.action" access="public">
<event-mapping event="pass" mapping="main.redirect"/>
<event-mapping event="fail" mapping="home.redirect"/>
<filter name="createEventBeanFilter">
<parameter name="model" value="project.model.ExampleModel"/>
<parameter name="facadeMethod" value="getEmptyTransferObject"/>
<parameter name="beanName" value="user"/>
</filter>
<event-bean name="user" type="project.model.ExampleModel" reinit="false"
fields="email,password"/>
<notify listener="userListener" method="login"/>
</event-handler>
The Filter takes three parameters:
Name | Description |
---|---|
model | The name of the Model object that has a method which returns a blank TransferObject |
facadeMethod | The name of the method which returns a blank TransferObject |
beanName | The reference in the event object that should point to this TransferObject |
With this code, the Filter will create a model
object, call its facadeMethod
to get the TransferObject and then assign it to beanName
within the event
object. The next line in the Mach-II configuration file calls the event-bean
command, which takes the same name
(which is the TransferObject) and populates it via the form inputs. Easy as pie.
The ColdSpring solution for Mach-II 1.6 or less is very similar to the non-ColdSpring solution in that they both use Filters and are both dead easy to implement. To proceed, let's assume again that the Model object we're working with has a method that returns an empty TransferObject. With that in mind, let's go ahead and make the TransferObject directly available through ColdSpring by using the factory-method
attribute:
<bean id="userModel" class="project.model.ExampleModel" singleton="false">
<constructor-arg name="transferFactory">
<ref bean="transferFactory"/>
</constructor-arg>
</bean>
<bean id="emptyUserObject" factory-bean="userModel" factory-method="getEmptyUser" singleton="false"/>
The getEmptyUser
method of the ExampleModel simply returns transfer.new("example.User")
.
Now for the filterEvent
method:
<cffunction name="filterEvent" access="public" returntype="boolean" output="false"
hint="I am invoked by the Mach II framework.">
<cfargument name="event" type="MachII.framework.Event" required="true"
hint="I am the current event object created by the Mach II framework." />
<cfargument name="eventContext" type="MachII.framework.EventContext" required="true"
hint="I am the current event context object created by the Mach II framework." />
<cfargument name="paramArgs" type="struct" required="false" default="#structNew()#"
hint="I am the structure containing the parameters specified in mach-ii.xml." />
<cfset var bean = paramArgs.bean />
<cfset var beanName = paramArgs.name />
<cfset var propManager = eventContext.getAppManager().getPropertyManager() />
<cfset var csProp = propManager.getProperty("coldspringProperty") />
<cfset var eventBean = '' />
<cfset var beanFactory = '' />
<cfif IsStruct(csProp)>
<cfset beanFactory = getProperty(csProp.getParameter("beanFactoryPropertyName")) />
<cfif IsStruct(beanFactory) AND StructKeyExists(beanFactory, "getBean" )>
<cfset eventBean = beanFactory.getBean(bean) />
</cfif>
</cfif>
<cfset event.setArg(beanName, eventBean) />
<cfreturn true/>
</cffunction>
This code is very straightforward in that it finds the ColdSpring service factory and looks up the correct bean.
Now, the event-handler
:
<event-handler event="login.action" access="public">
<event-mapping event="pass" mapping="main.redirect"/>
<event-mapping event="fail" mapping="home.redirect"/>
<filter name="createColdSpringEventBeanFilter">
<parameter name="bean" value="emptyUserObject"/>
<parameter name="name" value="user"/>
</filter>
<event-bean name="user" type="project.model.ExampleModel" reinit="false"
fields="email,password"/>
<notify listener="userListener" method="login"/>
</event-handler>
The Filter takes two parameters:
Name | Description |
---|---|
bean | The name of the ColdSpring bean which represents a blank TransferObject retrieved by the factory-method attribute |
name | The reference in the event object that should point to this TransferObject |
After the Filter call, the event-bean
command picks up the user
object from within the event
and populates it with the form inputs, leaving your TransferObject ready to be dealt with in the userListener
.
To get TransferObjects working as event-beans
in Mach-II 1.8 or greater using ColdSpring is by far the least amount of effort thanks to the new call-method
command. For more information on call-method
please see the call-method command M2SFP.
This approach makes the same assumptions as the Mach-II 1.6 with ColdSpring solution which used Filters. This time, however, we don't need to use the factory-method
attribute in the coldspring.xml
file, since the call-method
command does the method call for us. To review, ColdSpring has a bean configured like so:
<bean id="userModel" class="project.model.ExampleModel" singleton="false">
<constructor-arg name="transferFactory">
<ref bean="transferFactory"/>
</constructor-arg>
</bean>
The ExampleModel object has a method called 'getEmptyUser' that simply returns a blank TransferObject.
Now let's look at the (very simple) event-listener
entry using the new call-method
command:
<event-handler event="login.action" access="public">
<event-mapping event="pass" mapping="main.redirect"/>
<event-mapping event="fail" mapping="home.redirect"/>
<call-method bean="userModel" method="getEmptyUser" resultArg="user"/>
<event-bean name="user" type="project.model.ExampleModel" reinit="false"
fields="email,password"/>
<notify listener="userListener" method="login"/>
</event-handler>
After the call-method
command, the event-bean
command picks up the user
object from within the event
and populates it with the form inputs, again leaving your TransferObject ready to be dealt with in the userListener
.
If you're using Mach-II 1.8 Beta1 you must enable a specific parameter in your mach-ii_coldspring.xml
file in order to auto-wire to the call-method
command:
<parameter name="resolveMachIIDependencies" value="true"/>
Without this parameter enabled you will get the following error message:
A call-method commands was encountered that did not have a bean
named 'cfCallMethodCommand2ecfc1776128045$funGETBEANID@3e77f8' autowired into it.
Note that your error message might differ slightly from this one.