Mach-II Modules Don't Automagically Inherit ColdSpring Configuration

This is probably obvious to anyone who's spent time with Mach-II and ColdSpring, but I ran in to the issue the other day, spent about an hour missing the obvious, and thought I'd post.

If you have a Mach-II module, are using ColdSpring to manage dependencies in the parent application, the Mach-II module won't automatically inherit the ColdSpring setup. I had begun work on a simple module that didn't have any of its own objects to be managed by ColdSpring but instead would exclusively use objects managed at the parent application level. So I went about setting up a simple "home" view which needed some data from an object managed by ColdSpring in the parent app, and made a <call-method> command to it. I received an error stating that the bean I was looking for couldn't be found.

"Well that's not right," I thought. "I know the bean is in the parent app and other parts of the application are calling it just fine." After an hour or so of debugging, thinking that someone had broken the object in the latest build of the application, and getting a little annoyed, it dawned on me that I had never set up the ColdSpring property in this child module. Without the ColdSpring property being defined in the module, Mach-II had no idea how to call the bean I had specified in the <call-method> command.

So if you are creating a module that has no ColdSpring-managed objects of its own, but do want to use ColdSpring-managed objects in the parent application, you still have to set up a basic ColdSpring Mach-II property in your module, and point it to an (essentially) empty ColdSpring config file. Your ColdSpring config file can look like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans>
</beans>

Once that is in place (and as long as you also define the parentBeanFactoryScope in your module's ColdSpring property CFC), your module will have access to all the ColdSpring managed objects in the parent application.

Mach-II Call Method Command, How Do I Love Thee?

Now that Mach-II 1.8 is out, I figured I'd write a little bit about one of my favorite new features of the release, the <call-method> command.

Here's the problem that this new feature is designed to solve:

In a well-designed Mach-II application, you don't want to have a lot of business logic in your listeners. As a result, a lot of the methods in Mach-II listeners wound up being simple pass-throughs to a method in the service layer. This was repetitive, and tedious. Here's an example:

<cffunction name="getAllUsers" access="public" output="false" returntype="query">
<cfargument name="event" type="MachII.framework.Event" required="yes" />
<cfreturn variables.userService.getAllUsers() />
</cffunction>

We had to write a function that simply called a service layer method and returned the results. It's not hard to do. It is, however, clutter.

The <call-method> command eliminates the need for these kinds of placeholder method calls in your listener. Now you can do this in your Mach-II XML configuration file:

<call-method bean="userService" method="getAllUsers" resultArg="qryAllUsers" />

The "bean" referenced in this tag is a ColdSpring-managed bean that has already been auto-wired by the ColdSpring property in Mach-II. So this feature only works if you are also using ColdSpring (and — I've said it many times before — if you're not using ColdSpring or some other IoC container in your ColdFusion applications, you're costing yourself time and money.)

By using the <call-method> command, you've turned four lines of code into one. Not bad!

What if your service layer methods require arguments to be passed to them? That's easy, and Mach-II handles this.

<call-method bean="userService" method="getUser" args="${event.userID}" resultArg="user" />

The args attribute of the <call-method> command can take single or multiple, comma-separated values, or even an argument collection structure.

But what's the ${ } syntax? That's the expression language syntax that's built in to Mach-II. This feature arrived in Mach-II 1.5 under the name "Bindable Property Placeholders," and has evolved since then and is even more useful. It allows you to provide variables evaluated at runtime within the Mach-II configuration file. You can even provide default values in case the variable you're looking for in the expression doesn't exist (ie; ${event.userID:0}). So in the example above, it's going to to pass the result of evaluating event.userID (getting the value of the userID variable in the current event) to the userService.getUser() method.

I'm using the <call-method> command in my latest project using 1.8. It's saving me time, makes the event flow as defined in the Mach-II XML config file clearer, and eliminates a duplicate layer of abstraction in the app.

The official documentation for the <call-method> command can be found in the Mach-II wiki.

Lessons Learned in Making Many Mach-II Modules Out of Standalone Apps

This entry is about a month overdue, but given the holidays and Getting Stuff Done (SM) at work, it's been delayed.

As I mentioned in my first posting in the series, I'm in the process of converting a bunch of standalone Mach-II apps in to modules inside of a new, monolithic application. It's been a surprisingly smooth process, thanks to the tools that the Mach-II framework gives us to work with. Most of the bumps along the way have been related to bad software design practices in some of the oldest apps that I've created, and continuing, after many years building Web applications, to do dumb things like hardcoding URLs and other strings which will change when converting the app to a module (or moving the app to a different server).

Here are a few key lessons I've learned:

  • Use BuildURL and the View Tag Library generously. One of the major time sinks I've had to endure in this process is changing all of the links and constructed URLs in the apps-to-modules. If you're using Mach-II and still writing <a href>s in your views, you're throwing money out the window. Sooner or later, those URLs are going to change, and you're going to have to rewrite them. BuildURL and the View Tag Library encapsulate the creation of URLs/links in your views so that when you change servers (or need to roll out a new version of a module in a different directory), you do very little (to no) work to make the URLs/links all update nicely. And, yes, you can use them to create links in your inline JavaScript (if you use inline JavaScript).

  • Avoid redundancies in your ColdSpring configuration files. If you're going to share functionality across modules and you manage your object dependencies with ColdSpring, you're going to run in to instances where you define a bean multiple times in multiple ColdSpring configuration files. This is a really common situation. If you have a userService, for example, you're probably going to refer to this userService throughout the modules in your application. It's easy to define that userService in each of the ColdSpring configuration files in each of the modules that uses the userService, but don't. Simply define the userService bean in the ColdSpring configuration file at the top level of the application, and then if you inherit beans defined in the parent (top level) app as discussed in this post, you have access to that userService in your module. You don't need to define it again, and doing so is wasteful, and may lead to subtle (or not so subtle) issues when the a new version of the service is rolled out in the parent but your child is using an old version of the service because it's been define that way in the child ColdSpring config file. If your module needs a specific version of the service (eg; it needs to keep an old version for compatibility with something else), you can define it in the child ColdSpring config file as needed.

  • Avoid per-module session objects. This is a really easy trap to fall in to if you are porting existing Mach-II applications to modules inside a new, larger application. Most of your existing apps will have information stored in the session scope. It's tempting to keep your perfectly fine code as-is and just create new session variables as needed in each of your apps-to-modules. The problem here is that if you're creating new session variables in 7 or 15 or 22 modules, you're going to start to eat up memory and, more importantly, you're going to have to try to track down why things are going wrong in 7 or 15 or 22 session variables rather than in a single location. It's just bad form to not encapsulate everything you need in the session scope in a single variable created and managed at the top layer of your application. In all likelihood, all those redundant session variables generated in your modules largely contain the same information — userIDs, user objects, security or role-permission information, and the like. Managing this in one place makes a heck of a lot more sense than spreading it out in to each of your modules.

  • Handle exceptions and errors at the main app level. As with redundant bean definitions or per-module session objects, per-module exception and error handling is most often redundant. While there may be cases where you want to handle an exception in a special way in a module, there's no good reason to have different exception or error views in each module. Provide a consistent way of handling errors and exceptions for the developers behind the app and a consistent experience for the people using the app. If you want to do some module-specific handling of errors (to try to recover, to log something specific to the module, etc.), you can do this in your module's Mach-II XML config file:

    <event-handler event="exception">
    <filter name="tryToRecoverFromExceptionFilter" />
    <-- OR -->
    <notify listener="someListener" method="doSomeErrorProcessing" />
    <-- FINALLY -->
    <announce module="" event="exception" copyEventArgs="true"/>
    </event-handler>


  • Get rid of the extra junk. Not using all the plugin points you stubbed out in your plugins? (Or, in my case, left in there because you copied from the samplePlugin.cfc that came with the framework?) Get rid of them. No longer using that sessionFacade.cfc because you're managing your session objects centrally? Get rid of it. Get rid of everything you're not using. It's good practice and will make things cleaner and easier to understand for the poor sap who has to work on the app your absence.

So that's it for this series. I have to give major props to the Mach-II team for making it so easy to convert existing Mach-II applications to Mach-II modules. It's made this big project so much easier.

Mach-II Modules and Views

It looks like I'm going to spend another Monday morning writing about Mach-II modules. In this entry in the series, I'm going to look at how your CFML-based views are affected by the Mach-II module structure.

Mach-II provides a lot of convenience methods for handling views. In Mach-II 1.8, there's a whole new set of tag libraries which make tying your views to other actions in a Mach-II application even simpler. Of particular interest are the <form> and <view> tag libraries as they make dealing with mundane tasks like binding form fields to event objects and creating links simpler and more flexible under the Mach-II framework. Prior to Mach-II 1.8, there were the BuildURL() and BuildURLToModule() methods, which were your biggest friends in building links within and between modules in a Mach-II app.

I realize that not everyone likes to have a framework write their URLs for them. There are issues of convention, framework agnosticism, SEO optimization, and individual style that can and often should be factored in. However, if you're using Mach-II modules or Mach-II SES URL rewriting, or the new routes support in Mach-II 1.8, it would be quite unwise to write URLs on your own. I'd argue that as you've made the investment in the framework to begin with, you should let the framework generate your URLs for you via its various convenience methods in all cases. If you ever switch domains or even move the base URL path of your application or need to swap one module for another, changing a single value in your Mach-II XML config file is going to be a lot faster (and a lot less error prone) than doing a search and replace on every <a href> in your code base.

(As a side note, I've had issues with outputting BuildURL() calls within JavaScript blocks, and did use that as an exception to the above rule. However, the new buildUnescapedUrl() function introduced in Mach-II 1.8 eliminates that exception.)

Using BuildURL() and BuildURLToModule()

By using BuildURL() and, more relevant to this post, BuildURLToModule(), you let Mach-II handle the writing of your <a href> tags. These functions are available in views only, and not inside plugins, filters, or listeners. If you use Mach-II modules, you're going to want to use BuildURLToModule() over BuildURL() in most cases. Here's why:

  • BuildURLToModule() ensures that the module is preprended to the event name in the resulting URL. This ensures that the user is routed to the right module when the event is requested.
  • BuildURLToModule() allows for an empty module name in the module argument of the function. This lets you write events which point to the main/parent application and forces the request to go to the main/parent application.

If you just use BuildURL(), Mach-II is going to assume that you want an event in the same/current module. This works fine if you are at the top level of your application, in the main/parent portion of the application. This doesn't work so well if you're inside a module. For example, if you have an event called "logout" tied to a "Logout" link and you are inside a module and a user clicks on the "Logout" link, BuildURL() is going generate a link that looks for an event called "logout" in the current module. As the logout event is (probably) defined in the main/parent application, Mach-II won't be able to find the event in the current module, and an exception will be thrown.

As a result, it's a good idea to use BuildURLToModule() instead of BuildURL() in both the parent and child (module) application views. This way, you ensure that the user is being routed to the right event in the right module, even if it's the default/parent module. This is my personal preference, and not an official guideline of Team Mach-II.

Using the <view> Library

The <view:a> tag is a handy way to let Mach-II write your <a href> links. It does much of what the BuildURL() and BuildURLToModule() functions have done for some time now, only in a simpler and more elegant wrapper. Now that I'm using Mach-II 1.8 to build my applications (and for this particular, module-heavy project), I prefer it to BuildURL() for generating <a href>s. I still use BuildURLToModule() when I'm not generating a <a href> tag.

There are lots and lots of options when using the <view:a> tag, but the basic format of generating a link is as follows:

<view:a module="blog" event="showEntry" p:entryId="456"></view:a>

You simply specify the event name in the event attribute, the module name in the module attribute, and any key/value pairs you want to add to the link in the p attribute. If you have multiple key/value pairs you want to add, just put multiple p attributes in the tag, as follows:

<view:a module="blog" event="showComments" p:entryId="456" p:commentID="89"></view:a>

You can put CF variable names in lieu of static values, or use the expression language syntax available in Mach-II to generate the values:

<view:a module="blog" event="showComments" p:entryId="${event.entry.entryID}" p:commentID="89"></view:a>

Again, you should provide a blank value for the module attribute if you want the link to point to an event in the main/parent application. This ensures that Mach-II looks for an event in the main/parent application and not in the current module.

Using the <form> Library

When it comes to generating form action attributes, the <form> tag library works just like the <view:a> tag. An example:

<form:form actionEvent="createEntry" actionModule="blog">

It's very unlikely you'd be doing a form post from a module back to the parent/main application. As a matter of fact, I can't think of a good use for it. It's still a good idea to specify the module in which the event exists via the actionModule attribute, however, to ensure that the link is always written properly.

There's lots of documentation for the <form> tag library, so please review it to see what it's capable of. Binding, for example, is just plain useful and cool.

Reducing Most of the Above to a Single Point

The key point is this: no matter how you write your URLs, always make sure that a module (or an empty string for a module) is specified. This will ensure that event requests are routed to the right place. Start doing this when you begin to build your views, and you'll find it saves you time down the road when you're connecting everything.

Mach-II Modules and ColdSpring

Ah, ColdSpring. In my opinion, you're the bees' knees. You're what makes managing object dependencies a breeze. You simplify and enable the development of object-oriented ColdFusion apps so much that you're core to most large-scale ColdFusion applications written in the last couple of years. The teams behind Mach-II, Model-Glue and ColdBox like you so much that they made you a key player in their framework stacks. I'm not going to sell you on the virtues of ColdSpring any longer, so if you're not using it already, start please.

The key to using ColdSpring in Mach-II modules is the super-fantastic ColdSpring property that comes bundled with Mach-II 1.6 (and later). You need to be using the ColdSpring property and not the old ColdSpring plug-in which was available prior to Mach-II 1.6. I know that updating core infrastructure components like frameworks can be scary, but Team Mach-II has done a great job with making Mach-II backwards-compatible to the 1.1 version, so upgrading to take advantage of newer framework features shouldn't be too scary.

If you need to learn how to get the ColdSpring property working in your Mach-II application, please review the documentation.

The key point about using ColdSpring in Mach-II modules is that you can both set up individual ColdSpring factories for each module in your application and have access to a main, or parent, factory defined at the base level of your application. This is quite powerful for two reasons:

  1. ColdSpring managed objects become specific to the module in which they are needed, and don't clutter up the rest of the application, and,
  2. Commonly used objects can be created at the main/parent application level and be re-used throughout the entirety of the application, as needed.

In order to use ColdSpring in a module, you need to create a ColdSpring property and define it in your Mach-II XML configuration file for the module:

<mach-ii version="1.8">
<!-- INCLUDES -->
< includes>
< include file="/myApp/modules/sampleMod/config/sampleMod-coldSpringProperty.xml" />
</includes>

...listeners, events, plugins, etc...

</mach-ii>

The basic information you need to provide in your coldSpringProperty.xml file to use ColdSpring in a module is as follows:

<mach-ii version="1.0">
<properties>
      <property name="coldSpringProperty" type="coldspring.machii.ColdspringProperty">
         <parameters>
            <parameter name="beanFactoryPropertyName" value="serviceFactory"/>
            <parameter name="configFile" value="/path/to/my/coldSpringServicesFile.xml"/>         
            <parameter name="configFilePathIsRelative" value="false"/>
            <parameter name="resolveMachIIDependencies" value="true"/>
            <parameter name="parentBeanFactoryScope" value="application"/>
            <parameter name="parentBeanFactoryKey" value="serviceFactory"/>
         </parameters>
      </property>
</properties>
</mach-ii>

Here's what each parameter does:

  • beanFactoryPropertyName — This one isn't required, but you really should give your ColdSpring bean factory a name. You must do this in the ColdSpring property XML file for your base application, for reasons I'll describe below.
  • configFile — This is the path to the ColdSpring XML file for your module, in which ColdSpring-managed beans are defined. This one's required.
  • configFilePathIsRelative — This lets Mach-II know if it should be using an absolute or relative path to find the ColdSpring XML file defined above. Defaults to false.
  • resolveMachIIDependencies — This defaults to false, but if you plan on using the super-convenient Mach-II autowriring introduced in Mach-II 1.6, then this must be set to true, or nothing gets autowired in to your plugins, listeners, or filters.
  • parentBeanFactoryScope — Here's where you tie in the bean factory from your base/parent Mach-II application to your module. By setting this to true, you complete one half of the steps needed to pull in beans defined in the base application but that are used across your whole application. This merely defines the scope in which your parent bean factory is stored. That's most often going to be the application scope.
  • beanFactoryPropertyName — The value provided here must match the beanFactoryPropertyName value defined in your parent/base app. If it does, you've completed the second half of the steps needed to pull in beans defined in the base application but that are used across your whole application.

So how does all of this work in practice?

I'm not going to go in to how you define ColdSpring-managed beans in the ColdSpring XML file. That's all in the ColdSpring documentation. I'll show you how you can use the beans defined in your base app in your module.

Let's say you have a userService that is defined in your base ColdSpring XML file. This is a pretty common scenario, as you'll most likely need that userService (for things like getting user information) throughout your application, and in all of your modules. Here's the userService defined in your base ColdSpring XML file:

<beans>
   <bean id="userService"
      class="path.to.userService">

      <constructor-arg name="dsn">
         <value>${dsn}</value>
      </constructor-arg>
   </bean>

...more beans defined here...
</beans>

In your module's ColdSpring XML file, you can then utilize the userService like this:

<beans>
   <bean id="reportingService"
      class="path.to.reportingService">

      <property name="userService">
         <ref bean="userService"/>
      </property>
   </bean>

...more beans defined here...
</beans>

You don't define the userService in your module's ColdSpring XML file. Because you've defined it in the parent/base app's ColdSpring XML file, and you set the parentBeanFactoryScope property to "application" (because that's where the parent bean factory is stored) and you set the beanFactoryPropertyName property to the same value as defined in the parent/base app's ColdSpring XML file, Mach-II's ColdSpring property is smart enough to figure out how to grab the userService and put it in the right place.

This is just one example. You can use as many beans from your parent/base app in your modules as you need. By defining commonly used beans in your parent/base app, you reduce your application's overall memory footprint and encourage good object reuse.

One Big Caveat

There's one thing you really need to watch out for when using ColdSpring and Mach-II modules: bean name collisions. If you define a bean in your parent/base app called "page" and you also define a bean in your module ColdSpring XML called "page," guess which one is going to get used in your module? I'd recommend that you always using module-specific bean names for the beans defined in your module's ColdSpring XML file. This way, you can ensure that you're not going to run in to naming collisions.

A Few Other Options

You can put the bean factory from your module back in to the parent scope if you need to utilize the module bean factory in the parent/base app. You may have bits of functionality encapsulated in a module that need to be utilized elsewhere in your application. For example, if you have a threaded discussion module, you may want to display the 10 most recent posts on your application's home page. As the functionality for doing this is already defined in a bean that is managed by the threaded discussion module's ColdSpring bean factory, you may want to use that bean in the parent/base application rather than defining it again in the parent/base application's ColdSpring XML file.

To accomplish this, you need to add this one line to your module's ColdSpring property file:

<parameter name="placeFactoryInApplicationScope" value="true"/>

This places the module's ColdSpring bean factory in the parent application's application scope. As explained in the Mach-II documentation for the ColdSpring property, Mach-II appends the name of the module to the beanFactoryPropertyName value when the module's ColdSpring bean factory is added to the parent's bean factory to avoid beans in the module overwriting those defined in the parent. Pretty smart stuff!

Finally, you can use ColdSpring in your module, but not worry about utilizing beans defined in the parent/base application at all. You lose some power and code-reuse, but you may have no good need to use the parent bean factory. In that case, just omit the parentBeanFactoryScope and parentBeanFactoryKey parameters from your module's ColdSpring property XML file, and you're good to go.

Whew! These entries are turning out to be longer than I expected. But there are more to come!

Mach-II and Module Config Files

In this installment of my mini-series on modules in Mach-II, I'm going to talk about the Mach-II XML config file for your module, how it relates to the parent (main application) Mach-II XML config file, and try to offer some tips on what should and should not go in your module's Mach-II XML config file.

Before I go any further, it's important that you understand the context of this particular series on Mach-II modules and the fact that, for my specific purposes, I am not talking about building modules which are intended for drop-in deployment into any Mach-II app anywhere, like the fantastic Mach-II Dashboard. I'm focusing on building modules that represent mini-applications operating under the auspices of a larger application. Examples of these modules would be a threaded discussion system in a larger community site, or an exam system in an online course site.

A Sample Module XML Config File

Below is a really simple Mach-II XML config file for a module.

<mach-ii version="1.8">
   <!-- INCLUDES -->
   < includes>
      < include file="/myApp/modules/sampleMod/config/sampleMod-coldSpringProperty.xml" />
   </includes>
   
   <!-- PROPERTIES -->
   <properties>
      <property name="applicationRoot" value="/myApp/modules/sampleMod/" />
      <property name="defaultEvent" value="sampleModHome" />
   </properties>

   <!-- LISTENERS -->
   <listeners>
      <listener name="simpleListener" type="myApp.modules.sampleMod.model.simpleListener" />
   </listeners>
   
   <!-- EVENT-FILTERS -->

   <!-- EVENT-HANDLERS -->
   <event-handlers>
      <event-handler event="sampleModHome" access="public">
         <notify listener="simpleListener" method="getRandomNumber" resultArg="randomNumber" />
         <notify listener="simpleListener" method="getHTTPString" resultArg="httpAddress" />
         <view-page name="sampleModHome" />
      </event-handler>
      
      <event-handler event="sampleProtectedEvent" access="public">
<filter name="adminsOnlyFilter" />
         <view-page name="sampleProtectedView" />
      </event-handler>
   </event-handlers>
   
   <!-- SUBROUTINES -->
   
   <!-- PAGE-VIEWS -->
   <page-views>
      <!-- View Loaders -->
      <!-- This loads all pages which match /views/**/*.cfm, so a page-view called aboutUs.index would translate to /views/aboutUs/index.cfm -->
      <view-loader type="MachII.framework.viewLoaders.PatternViewLoader" />
      <!-- Define all other page-views here -->
   </page-views>
   
   <!-- PLUGINS -->
   
</mach-ii>

<includes>

You can include other configuration XML files in your module XML configuration file with no problem. In this example, I've included a Mach-II property for ColdSpring because there are certain objects that I want managed by ColdSpring and that are only pertinent to this module. I'll talk more about Mach-II modules and ColdSpring in an upcoming post, but I did want to show you how you'd configure the module to utilize ColdSpring, should you need to (and you most likely will).

<properties>

At a minmum, you need to include two properties in your module's Mach-II config file:

  1. applicationRoot — The absolute path to your module under Webroot
  2. defaultEvent — The default event to call should no event be specified by a request

Notice that the following Mach-II properties you would have in a Mach-II XML configuration are not listed here:

  • eventParameter
  • parameterPrecedence
  • maxEvents
  • MACHII_CONFIG_MODE
  • urlBase or any of the URL rewriting properties

All of these properties have to be defined at the main application level and cannot be overridden at the module level.

You can include the following properties in your module's Mach-II XML config to override what's being used in the main application:

  • exceptionEvent
  • Module-specific caching property
  • Module-specific logging property

While you can include these here, I would recommend that you handle exceptions, caching and logging at the main application level. This way you consolidate your exception handling, caching and logging setup in to a single location and don't wonder why you've set up logging in the main application but it's not logging as expected in the module due to a configuration override there. There are perfectly legitimate reasons to handle exceptions at the individual module level. I just prefer to do it centrally.

<listeners>

You define listeners here exactly as you would in a main Mach-II XML configuration file. Nothing special here. Move on.

<filters>

Any filters that are needed for this module and this module only are defined here. You'll notice in my configuration file that I call a filter in the sampleProtectedEvent event, but I don't define any such filter in this XML config file. How can this possibly work?

Your module inherits all of the properties, event filters, and plugins defined in your parent (main) Mach-II configuration file. So if you have a "admins only" filter which you use to only let administrators access certain events in your application, define it in your parent (main) Mach-II configuration file. You can then refer to that filter in any of the module Mach-II configuration files that operate under the parent app. This is much cleaner, simpler, and more encapsulated than defining a "admins only" filter in each of your modules. You're free to write a special "admins only" filter for a specific module that has special requirements, of course, and you'd define it in this block if that is what you need to do.

<event-handlers>

Event handlers are defined exactly as they are in the main Mach-II XML configuration file. Nothing special here. Move on.

<page-views>

As with listeners and event-handlers, page views are defined exactly as they are in the main Mach-II XML configuration file. The <view-loader> introduced in Mach-II 1.8 is a godsend and helps to remove all of the endless page-view declarations you'd normally find in a Mach-II XML configuration file, and if you're not using it, you should.

<plugins>

As previously mentioned, your module inherits all of the properties, event filters, and plugins defined in your parent (main) Mach-II configuration file. As such, you'd find common plugins like "is the user logged in plugin" and "put properties from a property CFC in to the current event plugin" defined at the main application level. Every event in your module would be subject to the plugins in your main application running on each and every event in the module, just as they do in the main application.

If you have a plugin which needs to run on every event in your module, define it here and it will work as expected.

There is one additional configuration option you can provide for plugins in a module, and that's the runParent attribute. This attribute tells Mach-II when your module plugins should be run in relation to the plugins in the parent/main app. As I mentioned in my first post in this series, I haven't used this myself because I keep to the default behavior in this regard. I'll simply quote from the Mach-II docs and you can take it from there:

Defines if or when parent (base application) plugins should be run. This attribute only applies to Mach-II module XML configuration files and has no effect if defined in a base application. A value of after will run the defined module plugins first then the parent plugins, a value of before will run the parent plugins first then the module plugins and a value of none will run only the module plugins with no parent plugins whatsoever. The default value is after if not defined and the XML configuration file is a module.

Including Your Module's Config File in the Main App Config File

The developers behind Mach-II made it super easy to add a module to any existing Mach-II application. To add a module to an existing Mach-II application, you simply need a <modules> block which tells Mach-II where to find each module you want to include.

...all the rest of your main Mach-II config file, including plugins, listeners, events, etc...
<!-- MODULES -->
<modules>
<module name="sampleMod" file="/myApp/modules/sampleMod/sampleModConfigFile.xml" />
</modules>

That's it. It takes nothing more than a single line of XML to include your super-complex module in your main application.

If you want to pass startup parameters to your module, you can do that too. Here's the example from the Mach-II Dashboard module:

<module name="dashboard" file="/MachIIDashboard/config/mach-ii_dashboard.xml">
<mach-ii>
<properties>
<property name="password" value="superSecretPassword" />
</properties>
</mach-ii>
</module>

The name that you give the module here (in the name="" attribute) is really important. The name gets prepended (that is, put before) every event name generated by Mach-II functions such as BuildURL(), BuildURLToModule(), and the <view:a> tag. You also have to include that module name in any links you hand-build to the other events in your module, or other modules in the larger application. It's what tells the framework to look for an event within a specific module, and not in the main Mach-II app itself. So an event name like "dashboard:main" would look for an event called "main" inside of the "dashboard" module. To use the sample module XML config file above, an event with the name "sampleMod:sampleProtectedEvent" would look for the event "sampleProtectedEvent" inside the module named "sampleMod."

If you fail to prepend the module name (or identifier) to the event name, Mach-II looks for an event with the given event name in the the main application. You'll most likely end up with a "missingEvent" exception as a result.

I'm going to cover modules and Mach-II views in more detail in an upcoming post. In the meantime, if you have any questions, comments, or corrections (I'm looking at you, Peter!), please post away!

A Brief Series on Mach-II Modules

I've been doing a lot of work with modules in Mach-II in the past couple months and thought I'd blog about some of the stumbling blocks I've encountered.

First, a little background:

A Mach-II module is an application which runs inside of a parent Mach-II application. A module usually groups together common functionality in to a related package, encapsulates that package nicely within your Mach-II app, and inherits a lot of the base configuration from the parent Mach-II application. Some people build Mach-II modules that are meant to be reused across multiple applications, for multiple sites, while others build open-source modules which are meant to be redistributable and used by anyone, anywhere. My situation is a little different.

I'm in the process of re-architecting one of our major applications, getting years of old cruff and rebuilding everything on top of Mach-II 1.8. A large part of this project entails taking some existing Mach-II apps (build under 1.1, 1.5 and 1.6) and integrating them in to the new, main Mach-II 1.8 app. A good amount of the work involves throwing out what's handled by the parent app, adding in new parent-child integration points where needed, and refactoring views to get them to work inside of modules.

So that's the context for my work. Your experience may be different, of course, especially if you are looking to build a standalone module that can be integrated in to anyone's Mach-II app, like the super-fantastic Mach-II Dashboard application or Mach Blog. (BTW: If you develop with Mach-II and you're not using the Mach-II Dashboard application, you're wasting time and effort. Single-listener, plugin, filter or config reload is a huge timesaver — and that's only part of what you can do with the Dashboard.)

The full introduction to Mach-II modules can be found on the Mach-II wiki. I'm going to assume that you're quite familiar with how Mach-II works and know your way around a Mach-II configuration file.

I also want to say that I've not used each and every feature supported by Mach-II modules. I've not had need (yet) for things like the runParent attribute of the <plugin> tag. If you're interested in that topic, please see this thread on the always-useful Mach-II Google group.

There's a lot of ground to cover, so I thought I'd break things apart in to a series of posts:

  • Modules and Mach-II config files
  • Modules and ColdSpring
  • Modules and Views
  • Converting an Existing App to a Module

I'm going to try to get one of these out a week. In the end, if Team Mach-II finds it interesting, useful, and remotely accurate, I'll consolidate some of this to post on the Mach-II wiki.

Using Mach-II Property CFCs and ColdSpring

One of the things that I really like about using Mach-II, my MVC framework of choice for developing ColdFusion applications, is the level of support and interaction with the community that the core team behind the framework provides. I know it's a lot of work and a lot of time for them (and time is the one thing that none of us ever have enough of), and I'm always impressed with how they go out of their way to help boneheads like me in our hour of need.

One of the nifty features introduced in Mach-II 1.5 are property CFCs. In addition to supporting structs and arrays of properties in your Mach-II XML configuration file, you can also use CFCs if you want to do more advanced property setup. A common usage scenario for property CFCs is to store application globals. Prior to Mach-II 1.5, you'd create and store application globals in the configure() method of a plug-in, but that's really not what plug-ins in Mach-II are for. Plug-ins intercept execution points on each request, and weren't originally intended for application global functionality, but that's the hack that the community developed to work around a limitation of Mach-II.

When I first used property CFCs to set up application globals, I ran into a bit of a problem. My existing application globals were defined in an "appConstants" plug-in. The plug-in utilized objects managed via ColdSpring, because that's how I manage my object creation and dependency injection in CF (and, really, if you're not using ColdSpring, you really should). I needed to pull in a geographyService object from ColdSpring so I could put a query of states and query of countries in to my app globals. Pretty standard stuff.

Here's what I learned about doing this using Mach-II property CFCs:

1. Property CFCs are managed by Mach-II and not by ColdSpring. This is super-important and what really tripped me up.

Oftentimes in ColdSpring-managed objects where we're doing property injection (rather than constructor argument injection), we'll have something that looks like this (truncated for brevity):

<cffunction name="init"...>
<cfset variables.geographyService = 0 />
</cffunction>

<cffunction name="setGeographyService">
<cfargument name="geographyService">
<cfset variables.geographyService = arguments.geographyService />
</cffunction>

This is pretty standard stuff for ColdSpring object injection. The trouble is that this doesn't really work for property CFCs in Mach-II 1.5 or later. Mach-II needs to handle the injection, not ColdSpring, so if you rely on ColdSpring to do this, you'll always have a variables.geographyService that = 0.

The solution? Use Mach-II 1.6's very handy "depends" autowiring enabled via Mach-II 1.6's ColdSpring property. If you're using Mach-II 1.6 or later you should be using the MachII.properties.ColdspringProperty instead of the older, and deprecated, ColdSpring plugin. The ColdSpringProperty in Mach-II 1.6 includes the ability to add the attribute "depends" to your <cfcomponent> declaration and it will autowire whatever objects you specify in to that object, without any additional calls or references to the ColdSpring framework. It's really sweet and very handy.

So for my appGloabls property CFC, my <cfcomponen> tag looks like this:

<cfcomponent displayname="appGlobals" extends="MachII.framework.Property" depends="geographyService" output="false" hint="I set up some app constants used throughout the life of the app.">

The depends="geographyService" attribute autowires the geographyService (managed by ColdSpring) in to this property CFC. Now I can call it and use it just as I did back in the days of my appConstants plug-in. For example:

<cfset globalVars.qryStateProvinceList = variables.geographyService.getStateList() />
<cfset globalVars.qryCountryList = variables.geographyService.getFilteredCountryList() />

2. If your property CFC depends on ColdSpring in any way, you need to declare that property CFC after the ColdSpring property declaration in your main Mach-II XML config file.

The always-helpful Peter J. Farrell (from the Mach-II team) explained to me that if you use <includes> in your Mach-II XML config file, as is the default example and method for utilizing the MachII.properties.ColdspringProperty, properties defined in the <includes> are parsed after the properties defined in your main Mach-II XML config file. So if you declare a property CFC that utilizes ColdSpring in your main Mach-II XML config file, it's going to get parsed and processed before the ColdSpring property gets parsed and set up, so the "depends" autowiring just won't work.

The solution? Make a really simple include for your property CFC or CFCs that use ColdSpring and declare that after your MachII.properties.ColdspringProperty include, as follows:

<includes>
<include file="config/coldspringProperty.xml" />
<include file="config/appGlobalsConfig.xml" />
</includes>

Utilizing this setup, I'm now able to utilize ColdSpring dependency injection and properly move my application globals setup in to a property CFC where it belongs.

BlogCFC was created by Raymond Camden.

Creative Commons License
The content on http://www.iterateme.com/ is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.