Dave Ross

Subscribe to Dave Ross: eMailAlertsEmail Alerts
Get Dave Ross: homepageHomepage mobileMobile rssRSS facebookFacebook twitterTwitter linkedinLinkedIn


Related Topics: ColdFusion on Ulitzer, CEP on Ulitzer

CFDJ: Article

ColdFusion Developer's Journal Special "Frameworks" Focus Issue: Mach-II

Retrofitting the Pet Market application

Examining the M2PetMarket
There is, of course, a DTD available for the mach-ii.xml file, but the set of elements and sub-elements is fairly simple. Let's look at the individual elements in the configuration file for the M2PetMarket application. We start with properties.

<properties>
    <!-- Standard Mach-II application settings. -->
    <property name="applicationRoot" value="/M2PetMarket" />
    <property name="defaultEvent" value="home" />
    <property name="eventParameter" value="event" />
    <property name="parameterPrecedence" value="form" />
    <property name="maxEvents" value="10" />
    <property name="exceptionEvent" value="exceptionEvent" />
    <!-- PetMarket application settings. -->
    <property name="dsn" value="M2PetMarket" />
    <property name="locale" value="en_US" />
</properties>

The applicationRoot is set to /M2PetMarket, indicating that a directory, M2PetMarket, should be a subdirectory of your Web root. The defaultEvent is home. When users initially come to the site, this is the entry point to the application. We won't change the properties, eventParameter, parameterPrecedence, maxEvents, and exceptionEvent and, for the sake of brevity, won't go into in this article.

Two additional properties, dsn and locale, are specific to this application and aren't part of the standard Mach-II properties (as the others are). Once set, these properties are available to all registered Mach-II components (listeners, plug-ins, etc.). Placing properties in the configuration file allows us to change their values, if needed, without changing any existing (and tested) code.

Next, we register listeners in their section of the configuration file:

<listeners>
    <listener name="StoreListener"
    type="M2PetMarket.m2components.StoreListener">
    </listener>
    <listener name="CheckoutListener"
    type="M2PetMarket.m2components.CheckoutListener">
    </listener>
</listeners>

Here, we've decided to use separate listeners to handle functions related to the basic store and the check out process. Remember, listeners are CFCs and different methods in those CFCs will be called depending on the event and how we wish to handle that event. We'll look at those methods when we get to the section on event handlers.

Our event-filters section is empty - this simple application requires no event filters.

We have two entries in the plugins section, PetMarketPlugin and TracePlugin. The configure method of a plugin is called automatically by Mach-II when the application is first initialized and allows us to write any initialization code. In the case of PetMarketPlugin, configure creates an instance of the Store CFC and calls its superclass, MachII.framework.Plugin, to keep this object in persistent memory.

The Store CFC has the following capabilities:

  • init acts as a pseudo-constructor. It accepts a data source name (DSN) and makes it an instance variable.
  • getCategory accepts a category ID and returns a properly instantiated Category object.
  • getProduct accepts a product ID and returns a properly instantiated Product object.
  • getProductItem accepts a product item ID and returns a properly instantiated ProductItem object.
  • placeOrder accepts a User object and a ShoppingCart object and completes the placement of an order.
  • queryCategories returns a recordset of all categories.
  • queryProductItems returns a recordset of product items, optionally limited if an argument, productoid, is passed to this method.
  • queryProducts returns a recordset of products, optionally limited if an argument, categoryoid, is passed to this method.
  • searchProducts returns a recordset of products based on a keyword passed into this method.
  • getDSN returns the instance variable, DSN.
  • setDSN sets the instance variable, DSN.
All plugins extend the base Mach-II Plugin class, which has six plugin point methods. Individual plugins (such as PetMarketPlugin) may override any or all of these methods, depending on where the plugin developer wishes their code to called.
  • preProcess is called before any event processing.
  • preEvent is called before each event is handled. (A single HTTP request may encompass several events.)
  • postEvent is called after each event is handled.
  • preView is called before any view (i.e., display) page is rendered.
  • postView is called after any view page is rendered.
  • postProcess is called after all event processing.
  • handleException is called when an exception occurs (before any exception event is processed).
PetMarketPlugin overrides two of these plugin point methods, preProcess and preEvent. In the preProcess method, the plugin ensures that a User object exists in the session scope. In the preEvent method, the plugin places the Store object, the User object, and the locale (a property specified in the configuration file) in the current event.

The TracePlugin is used for debugging purposes and provides information about the events processed and the time each took.

In the event-handlers section, we specify how the application should respond to various events (see Listing 1).

These events encompass the entire functionality of the PetMarket application. Let's quickly look at how each event is processed.

The home event uses a page-view element. When a page-view is specified, Mach-II looks for the appropriate display page registered in view-pages (another section in the configuration file) and displays the file(s).

The preferences, about, legal, affiliate, category, product, showCart, and search events are similarly handled, including display pages registered in the view-pages section of the configuration file.

The updateUserPreferences event is a bit more interesting. It notifies a listener, StoreListener, that an event has occurred and passes the Event object to that listener's updateUserPreferences method. Before this, though, it specifies an event-mapping. This requires some explanation (see Sidebar).

To understand what event-mappings are and how they operate, look at the updateUserPreferences method of the StoreListener shown in Listing 2.

This method receives an argument of type, Event. In fact, virtually all listener methods receive this argument. The method then sets the User object's email, favoritepet, mailings_sales, and mailing_tips instance variables. When this is done, the listener places a status message in the event and is done with its work.

Where do we go from here? The original Pet Market application returns the user to the preferences page. Given this, we could announce a new event, preferences, from within the listener, since Listener objects can announce events. But to do so would tightly couple this listener with the flow of the application - something that would violate the very sound principle of loose coupling. Instead, the Listener object announces something quite generic, userPreferencesUpdated.

However, we have no userPreferencesUpdated event registered in our configuration file, nor do we wish to. Instead, we want to substitute our previously registered preferences event for the announced userPreferencesUpdated event, and that's just what using event-mapping allows us to do. When the StoreListener announces userPreferencesUpdated, Mach-II intercepts the event announcement, substituting in its place the event, preferences.

The event handler for addItemsToCart notifies the StoreListener object, calling its addItemsToCart method. The addItemsToCart method within the listener announces a cartUpdated event, for which we have a corresponding registered event.

The event handler for updateCart notifies the StoreListener object, calling its updateCart method. Again, the listener announces an event, cartUpdated. We have a registered cartUpdated event that introduces a new Mach-II XML configuration element, redirect. The redirect tag acts like the ColdFusion tag, cflocation, causing the browser to make a new HTTP request. The redirect tag is used to ensure that if the user clicks the "refresh" button on their browser, the updateCart method will not be called again.

More Stories By Hal Helms

Hal Helms is a well-known speaker/writer/strategist on software development issues. He holds training sessions on Java, ColdFusion, and software development processes. He authors a popular monthly newsletter series. For more information, contact him at hal (at) halhelms.com or see his website, www.halhelms.com.

More Stories By Ben Edwards

Ben Edwards is a Sun Certified Java Programmer and holds a degree in
computer science from the Georgia Institute of Technology. He
currently trains developers on software engineering practices
focusing on Java, object-oriented programming, and software
architectures. Ben is also cofounder of the Mach-II project.

More Stories By Matthew Woodward

Matt Woodward is Principal Information Technology Specialist with the Office of the Sergeant at Arms at the United States Senate. He was until recently a Web application developer for i2 Technologies in Dallas, Texas. A Macromedia Certified ColdFusion Developer and a member of Team Macromedia, he has been using ColdFusion since 1996. In addition to his ColdFusion work, Matt also develops in Java and PHP.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.