Skip to content

Using TransferObjects as Event Beans

thofrey edited this page Apr 1, 2014 · 4 revisions

by Mike Rogers (r.m.rogers@…)

Table of Contents

  1. Introduction
  2. Two and a Half Solutions
  3. Solution for Mach-II without ColdSpring
  4. Solution for Mach-II 1.6 or less using ColdSpring
  5. Solution for Mach-II 1.8+ using ColdSpring

Introduction

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?

Two and a Half Solutions

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.

Solution for Mach-II without ColdSpring

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.

Solution for Mach-II 1.6 or less using ColdSpring

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.

Solution for Mach-II 1.8+ using ColdSpring

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.

Gotcha with Mach-II 1.8 Beta1

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.

Clone this wiki locally