Release 1.2.1 (4/14/2005)
« Designing Event Handlers | Contents | Designing Plugins »
This section looks at designing and using event-filters. The
first issue to consider is whether you want an event filter or a plugin. Having
decided that you want an event filter, this section looks at some practical
reasons for using them to improve the structure and flow of your application
and then examines larger issues such as form handling, validation and security.
When you first start using Mach II, it can be difficult to tell whether you need an event filter or a plugin. You need to ask yourself a few questions about what you are trying to achieve so you can determine which will work better for the problem you are trying to solve:
If you answered "yes" to any of those questions, you probably need a plugin.
If you answered "yes" to any of those questions, you probably need an event filter.
If you answered "no" to all of the above but still think you need an event filter or a plugin, then read through the rest of this section and the next section, on plugins, and decide for yourself.
An event filter is a component that provides a filterEvent() method
which is called with three arguments:
eventeventContext<parameter> tags
in the filter invocationfilterEvent can either return true, in which case
processing of the event handler continues, or it can return false,
in which case processing of the current event handler is terminated (and processing
continues with the next event in the queue - unless you clear the event queue
by calling arguments.eventContext.clearEventQueue() in the filter). Usually,
when a filter returns
false, it will also announce a new event before doing so (if it
clears the event queue, it must announce a new event after doing so!).
An event filter can define a configure() method if it needs to
perform initialization - this is called automatically by the framework. Event
filters - like all other parts of the Mach II framework - are stored in application scope
so their instance variables are effectively application scope
variables.
A minimal event filter CFC looks like this:
<cfcomponent extends="MachII.framework.EventFilter"> <cffunction name="configure" returntype="void" access="public" output="false"> <!--- perform any initialization ---> </cffunction> <cffunction name="filterEvent" returntype="boolean" access="public" output="false"> <cfargument name="event" type="MachII.framework.Event" required="yes" /> <cfargument name="eventContext" type="MachII.framework.EventContext" required="yes" /> <cfargument name="paramArgs" type="struct" required="yes" /> <!--- return false if you need to abort the current event ---> <cfreturn true /> <!--- indicate success ---> </cffunction> </cfcomponent>
The filter is declared inside the <event-filters> .. </event-filters> tags in mach-ii.xml as follows:
<event-filter name="filterName" type="Path.To.YourFilter" />
where filterName is the name you want to give the filter within the mach-ii.xml file and Path.To.YourFilter is the fully-qualified component name for YourFilter.cfc.
You may optionally specify parameters for the filter declaration:
<event-filter name="filterName" type="Path.To.YourFilter"> <parameters> <parameter name="param1" value="value1" /> <parameter name="param2" value="value2" /> </parameters> </event-filter>
This provides default parameter values for param1 and param2 (of value1 and
value2 respectively). These can be accessed within the
filter using the getParameter() method - see below.
The filter is used in an event handler as follows:
<filter name="filterName" />
This causes the filterEvent() method to be called with the current event,
the current event context and an empty paramArgs struct.
You may optionally specify parameters for the filter usage:
<filter name="filterName"> <parameter name="param1" value="newValue1" /> <parameter name="param3" value="value3" /> </filter>
This causes the filterEvent() method to be called with the current
event, the current event context and a paramArgs struct containing
two keys: param1 with a value of newValue1 and param3 with
a value of value3. The intent is that parameter values
passed in this way would override any default parameter value specified in
the declaration of the filter. You would typically manage this with code
that looked like this:
<cffunction name="filterEvent" returntype="boolean" access="public" output="false">
<cfargument name="event" type="MachII.framework.Event" required="yes" />
<cfargument name="eventContext" type="MachII.framework.EventContext" required="yes" />
<cfargument name="paramArgs" type="struct" required="yes" />
<cfset var param1 = getParameter("param1") />
<cfset var param2 = getParameter("param2") />
<cfset var param3 = getParameter("param3") />
<!--- other var declarations --->
<cfif structKeyExists(paramArgs,"param1")>
<cfset param1 = paramArgs.param1 />
</cfif>
<cfif structKeyExists(paramArgs,"param2")>
<cfset param2 = paramArgs.param2 />
</cfif>
<cfif structKeyExists(paramArgs,"param3")>
<cfset param3 = paramArgs.param3 />
</cfif>
<!--- perform filter processing --->
<!--- return false if you need to abort the current event --->
<cfreturn true /> <!--- indicate success --->
</cffunction>
An event filter has access to both the current event and the current event
context so it is able to affect the logical flow of an application by manipulating
the event context. For example, when an event filter decides to abort the
current event (by returning false), it generally needs to announce
another event for the framework to execute and it may also decide to clear
any events that are queued up for execution. By convention, event filters
that do that usually have a parameter that specifies the event to announce
as well as a parameter that specifies whether or not to clear the event queue:
<cffunction name="filterEvent" returntype="boolean" access="public" output="false">
<cfargument name="event" type="MachII.framework.Event" required="yes" />
<cfargument name="eventContext" type="MachII.framework.EventContext" required="yes" />
<cfargument name="paramArgs" type="struct" required="yes" />
<cfset var invalidEvent = getParameter("invalidEvent") />
<cfset var clearEventQueue = getParameter("clearEventQueue") />
<!--- other var declarations --->
<cfif structKeyExists(paramArgs,"invalidEvent")>
<cfset invalidEvent = paramArgs.invalidEvent />
</cfif>
<cfif structKeyExists(paramArgs,"clearEventQueue")>
<cfset clearEventQueue = paramArgs.clearEventQueue />
</cfif>
<cfif someCondition>
<!--- note: clearEventQueue parameter is really a string --->
<cfif clearEventQueue is "true">
<cfset arguments.eventContext.clearEventQueue() />
</cfif>
<!--- pass current event's arguments into the new event: --->
<cfset arguments.eventContext.announceEvent(invalidEvent,arguments.event.getArgs()) />
<cfreturn false />
</cfif>
<cfreturn true />
</cffunction>
When user data enters your application, either as URL scope or form scope
variables, you generally need to perform some sort of data validation. This
may be as simple as checking that certain variables have been provided or it
may be something substantially more complex.
The Mach
II framework provides the RequiredFieldsFilter event filter
to allow you to check that a specified list of URL or form scope
variables are all present, announcing a specified event if one or more is
missing and returning false to abort the current event. This
filter takes two parameters:
requiredFields - a comma-separated list of field names that are to be checkedinvalidEvent - the event to announce if any fields are missingIf any fields are missing, the event that is announced has the same arguments as the current event plus the following additional arguments:
message - an error message (in English)missingFields - a comma-separated list of field names that are missing For more complex web form handling, you should create a bean to encapsulate
the web form data (and use the <event-bean> command to create
the bean object from the event arguments) and provide a validate() method
on that data object that returns a boolean. Then you can use the ValidateFormObject filter
(from corfield.org) to invoke that method and handle any failure. This filter
takes two or three parameters:
formObjectName - the name of the event argument on which to invoke validate()invalidEvent - the event to announce if validate() returns false clearEventQueue - optional, boolean, indicates whether to
clear the event queue before announcing invalidEventIf validate() returns false, the filter also returns false after
optionally clearing the event queue and then announcing the specified event
with the same arguments as the current event plus the following additional
argument:
formObjectName - the name passed into the filterIt is up to the specified event handler how to deal with reporting the validation failure, which is likely to involve additional calls to the data object and, therefore, additional event filter invocations to manage that interaction.
You can specify default parameters
for the ValidateFormObject event filter:
<event-filter name="barValidator" type="MachII.filters.ValidateFormObject"> <parameters> <parameter name="formObjectName" value="bar" /> <parameter name="invalidEvent" value="formHasInvalidBar" /> </parameters> </event-filter> <event-filter name="fooValidator" type="MachII.filters.ValidateFormObject"> <parameters> <parameter name="formObjectName" value="foo" /> <parameter name="invalidEvent" value="formHasInvalidFoo" /> <parameter name="clearEventQueue" value="true" /> </parameters> </event-filter>
You can now use these event filters without needing to specify the parameters each time or you can override the defaults:
<event-bean name="bar" type="my.model.bar" /> <filter name="barValidator" /> ... <event-bean name="foo" type="my.model.foo" /> <filter name="fooValidator"> <parameter name="invalidEvent" value="warnAboutBadFoo" /> <parameter name="clearEventQueue" value="false" /> </filter>
When user authorization needs to be performed on certain events, an event
filter is probably the simplest way to achieve this. The Mach II framework
provides the PermissionsFilter event filter to support simple permission-based
authorization checking. This filter checks that the current user has all of
the necessary permissions (specified as a comma-separated list) and if they
do not, it announces the event specified (after optionally clearing the event
queue). See the comments in the source code for more detail.
You may be able to simply extend the PermissionsFilter component
to implement your own security, overriding the getUserPermissions() method
to return a comma-separated list of user permissions. If you don't want the
default inclusive permission checking behavior, or your user permissions are
more complex than a comma-separated list, you can also override validatePermissions() and
implement your own checking.
If your security system is more complex than simple permissions, you will need to write your own security event filter.
« Designing Event Handlers | Contents | Designing Plugins »
Send me an e-mail when comments are added to this page | Comment Report
Current page: http://livedocs.adobe.com/wtg/public/machiidevguide/eventfilters.html