What's New in 2.6.0

This is a great new update for the ColdBox Framework & Toolkit. It includes several optimizations, bug fixes and some great new features and additions. For the entire list of tickets for this release, please visit the Release Notes.

Per-App Conventions

As you know, ColdBox works on conventions in order to work. However, in previous versions, you had to abide by the framework wide conventions. You could change them, but that meant ALL applications where the same. With per-app conventions you can declare the conventions YOU want for your app right on the configuration file. This increases the portabilities of the ColdBox applications, as now you can override any setting a framework-wide installation might have implemented and you are guaranteed that they will work. All you need to do is add the following to the coldbox.xml file:

<Conventions>
        <handlersLocation>_handlers</handlersLocation>
        <pluginsLocation>_myplugins</pluginsLocation>
        <layoutsLocation>_layouts</layoutsLocation>
        <viewsLocation>_views</viewsLocation>
        <eventAction>index</eventAction>                
</Conventions>
Important All conventions that are locations require a relative location to the Root of the running application. They cannot be set outside the root.

New Convention: Default Event Action

The framework now declares a default event action that will be executed on an event handler if not passed via URL/FORM/REMOTE. The default framework-wide convention is index. This means that you can create a new event called index in your event handler and the framework will call it implicitly. Example, create a new event handler called default then create a method on it called index. When you go to the URL: index.cfm?event=default or index.cfm/default, if the default event handler has the method index on it, it will execute it. How cool is that, your URL's can even become shorter now.

This update also applies to the SES interceptor.

New Convention: onMissingAction()

Again, the power of conventions. With this new convention you can now create virtual events that do not even need to be created in a handler. Every time an event requests an action from an event handler and that action does not exists in the handler, the framework will check if an onMissingAction() event action has been declared. If it has, it will execute it. This is very similar to ColdFusion's onMissingMethod but on an event-driven framework.

Below is the skeleton for such an event action:

<!--- onMissingAction --->
<cffunction name="onMissingAction" access="public" returntype="void" output="false" hint="on missing action">
    <cfargument name="Event" type="coldbox.system.beans.requestContext" required="yes">
    <cfargument name="missingAction" required="true" type="string" hint="">
    <cfset var rc = event.getCollection()>
        <cfscript>
                
        </cfscript> 
</cffunction>

This event has an extra argument: missingAction which is the missing action that was requested. You can then do any kind of logic against this missing action and decide to do internal processing, error handling or anything you like. The power of this convention method is extraordinary, you have tons of possibilities as you can create virtual events on specific event handlers.

Note: This convention will only fire if the missing action is on a specified event handler and the onMissingAction method exists on the handler. If not, the normal error handling routines will fire.

Debugger Settings

The ColdBox Debugger has been updated and it is in great transition to become an open API for further ColdBox versions. Versions 2.6.0 introduces the configuration of the debugger via the framework wide settings file or your application configuration file. Below is a snippet of what you can do:

<DebuggerSettings>
        <PersistentRequestProfiler>true</PersistentRequestProfiler>
        <maxPersistentRequestProfilers>10</maxPersistentRequestProfilers>
        <maxRCPanelQueryRows>50</maxRCPanelQueryRows>
        
        <TracerPanel    show="true" expanded="true" />
        <InfoPanel      show="true" expanded="true" />
        <CachePanel     show="true" expanded="false" />
        <RCPanel        show="true" expanded="false" />
</DebuggerSettings>

The panel sections are pretty self-explanatory, you choose which panels you want to display and which ones you want expanded by default. The first three settings are new and here are their explanation table:

Setting Type Framework Default Description
PersistentRequestProfiler boolean false If set to true, the framework will store request profiler information so you can view it via the new execution profiler panel
maxPersistentRequestProfilers numeric 10 The number of requests to store in the persistent profiler.
maxRCPanelQueryRows numeric 50 The number of rows to display on the RC panel when dumping queries.

Execution Profiler Monitor

A brand new monitor has been built that will keep a stack of the latest X requests and their execution profiles. This monitor can be kept side by side with your flex/AJAX or any frontend and give you execution profiles about your event model. By default the request profiler is turned off, so you will have to turn it on via the new Debugger Settings in your configuration file. You can also choose the max number of requests to persist execution profiles for.

Reinit Button on Info Panel

The info panel now has a button that you can use to reinitialize the framework. The panel even detects if you are using a reinit password or not. If you are it will ask you for the password and be off reinitializing your application with ease and comfort.

Autowire Interceptor Enhancements

The autowire interceptor has been enhanced to read an object's cfproperties and setter methods. You can now simply annotate or describe your dependencies via the cfproperty tag and the interceptor will inject them as you describe them. You can even choose the scope.

<cfproperty name="UserService"   type="ioc" scope="instance" />
<cfproperty name="LookupService" type="ioc" scope="instance" />
<cfproperty name="PublicService" type="ioc" scope="this" />

As you can see from the sample above, you annotate using three properties:

property value description
name string The name of the bean to inject
type ioc This indicator is what tells the autowire interceptor that this is a dependency.
scope string This is the scope to which to inject the dependency to. This can be anything you like, but it defaults to variables scope.

This enhancement is a more descriptive approach to your object dependencies than mere setter injections. Properties are read first and then setter methods will be discovered. So you can still use setter injection or a combination of both. The autowire interceptor will also work with any the object's inheritance tree.

Messagebox Plugin

The messagebox plugin has been revamped and has improvements in the following areas:

  • You can now override the storage scope without affecting all framework applications by setting a Custom Setting. Pre 2.6 you had the storage scope set in the framework wide settings.
Your Setting Values
messagebox_storage_scope session or client
<YourSettings>
  <Setting name="messagebox_storage_scope" value="session" />
</YourSettings>
  • You can now send an array of messages to the setMessage() method, the plugin will create a message string and they will be rendered with a <br/> Separator.
<cfset ErrorsArray = ArrayNew(1)>
<cfset ErrorsArray[1] = "The username is invalid">
<cfset ErrorsArray[2] = "The password is invalid">

<cfset getPlugin("messagebox").setMessage(type="info",messageArray=ErrorsArray)>
  • You can append messages to a currently set or unset message box. If you append to an empty messagebox the default type of information will be used.
<cfset getPlugin("messagebox").append('<br/> My Appended message')>
  • You can append an array of messages to a currently set or unset message box. If you append to an empty messagebox the default type of information will be used.
<cfset getPlugin("messagebox").appendArray(myMessageArray)>

Environment Interceptor

  • Added the ability to override ALL settings found in the configuration settings structure. If you would like to see the entire structure graph, you can dump the actual configuration structure and see the path of all the variables. For example, to override a particular nested setting, I can just say:
<Setting name="CacheSettings.ObjectDefaultTimeout"  value="0" />
<Setting name="Datasources.myAlias.password"  value="" />

Pretty handy! Please also see the Configuration Guide for a full list of the nested structures.

New plugin: json

A new plugin has been adapted for this release by Ernst Van der Linden and Sana Ullah. This plugin can encode/decode JSON syntax back and from coldfusion variables. It is super speedy and super awesome.

JSON notation for dynamic complex settings

The configuration file allowed for simple notation to create structures: {name:'luis',active:'true'} and arrays: [1,2,3]. However, with the inclusion of the new json plugin, you can now declare full JSON syntax notations on ALL complex variables in the configuration file. So you can inflate queries, nested arrays, structures and so much more. For a full syntax, see http://www.json.org/

*single quotes will be converted to double quotes by the processor, so it validates

<Setting name="myNestedSetting" value="{name:'luis', values:[1,2,3,4]}" />

<Setting name="myQuery" value="{'COLUMNS':['ID','CREATEDATE','ACTIVE'],'DATA':[['E9B6F2D1-B3D6-CB68-BBB327AFEF140A9B','March, 25 2008 23:12:03',0],['E9B6F2E1-E423-5409-C4146A3AA9AE0AF8','March, 25 2008 23:12:03',1],['E9B6F2F1-D076-074D-2292172D696107BA','March, 25 2008 23:12:03',1],['E9B6F300-A28E-0176-6B68C0B63CE65F4B','March, 25 2008 23:12:03',0]]}" />

New Plugin: MethodInjector

This is an advanced plugin to help you inject/remove UDF's and properties from coldfusion components. It also lets you invoke private methods on components. The applications for such a component are for dependency injection, runtime UDF additions or removals, unit testing, ability to call private methods from other contexts, etc. The plugin is available because it is internally used for internal core coldbox operations.

The utility methods that this plugin injects to a CFC are the following:

  • injectMixin(UDF) : To inject a method
  • removeMixin(UDF) : To remove a method
  • invokerMixin( method, [argCollection:struct],[argList:string]) : To invoke a method, no matter what scope it is.
  • injectPropertyMixin(propertyName,propertyValue,scope) : Inject properties to the variables or any scope you want.
  • removePropertyMixin(propertyName,scope) : Remove a property from the specifed scope and name.
//I have a cfc called UserService that I would like to do some injections/removals and call a private method

//Inject our utility methods
getPlugin("methodInjector").start(UserService);

//Let's inject the setController method
UserService.injectMixin( variables.setController );

//Let's set the Coldbox controller on it
UserService.setController( getController() );

//let's remove it now that its injected
UserService.removeMixin( "setController" );

//Now let's call a private method on the service called getTypes()
userTypes = UserService.invokerMixin("getTypes");

//Now another call with some arguments
userRole = UserService.invokerMixin("getDefaultRole",arglist="id='23423'");

//We are done, remove our utility methods
getPlugin("methodInjector").stop( UserService );

New SuperType Methods: htmlhead(), getInterceptor()

  • You can now easily use the cfhtmlhead tag from cfscript by just calling htmlhead(content). This method is available in all plugins, handlers, and interceptors.
htmlhead( myVar );

//or

htmlhead("<script src='includes/myScripts.js'></script>");
  • You can now interact with any loaded interceptors by executing the method: getInterceptor(interceptorclass). This is super useful if you would like to manually fire stages, interact with the interceptors or get data from them.
<cfset var myValidator = getPlugin("ioc").getBean("myValidator")>

<!--- Register a security validator --->
<cfset getInterceptor('colbox.system.interceptors.security').registerValidator( myValidator )>

IOC Plugin Updates

There are several updates to the ioc plugin that are shown below:

  • You can now override the ColdSpring or LightWire factory bean to use by setting the following in your Custom Settings in your configuration file.
    • ColdspringBeanFactory : Instantiation path to the ColdSpring factory.
    • LightWireBeanFactory : Instantiation path to the LightWire factory.
<Setting name="ColdspringBeanFactory" value="frameworks.coldspring1_2.beans.DefaultXMLBeanFactory" />
  • LightWire can be used with only a valid configuration file. Before, you had to declare the location of your base config object in the IOCDefinitionFile setting. This was the instantiation path of your lightwire configuration object. On 2.6 you can let go of that configuration object if you already have a valid coldspring xml file with all your bean declarations. The ioc plugin will detect that you want to use a coldspring xml file and it will create a base config object for you and make it parse this configuration file for you. So interchanging between colspring and lightwire in coldbox becomes even simpler.
<Setting name="IOCFramework" value="lightwire" />
<Setting name="IOCDefinitionFile" value="config/services.xml.cfm" />

This feature really makes life easier for the developer who is used to working with bean configuration files and doesn't want to create a configuration object. ColdBox will create one for you and configure it.

Applicationstorage and sessionstorage plugins updated

These two plugins have been updated in that each of them create a storage bin in their appropriate scopes. This permits the developer to now clear all variables or even get the entire storage scope. This in effect also protects other variables that might be on the scopes that are not in the ColdBox lifecycle. Finally, the code was optimized and they are actually faster!! New methods:

  • clearAll() : Clears the entire storage in the appropriate scope
  • getStorage() : Gets a structure with all the variables you have set in it.

SES: Numeric & Alphanumeric Routes

The SES interceptor has been updated to distinguish between numeric and alphanumeric routes. As of version 2.5.2 the interceptor only searched for alphanumeric routes, now you can do both. To do this, you simply add the following: -numeric to the variable place holder.

/:varname/ sets the varname as an alphanumeric placeholder
/:varname-numeric/ sets the varname as a numeric placeholder

Examples:

//Course 1
addCourse(pattern="blog/entry/:month-numeric/:year-numeric", handler="blog", action="displayEntry");

//Course 2
addCourse(pattern="wiki/:name/page/:page-numeric", handler="wiki",action="show");

The first course will look for a course that starts with blog/entry and then a numeric part and then another numeric part to create two variables: month & year.

The second course will look for a course that starts with wiki/ then a part that is alphanumeric and assign it to name, then page then another part that is a number an assign it to the variable page.

As you can see, with this feature, you can increase your custom routing system.

New Interceptor: Security

This feature can still change its implementation.

You can now add roles, permissions or any criteria based security easily to your applications. The premise of the interceptor is that it will protect incoming events and as it fires on every preProcess execution point. The interceptor validates the incoming event against a set of rules that you define and then if a rule is matched will try to see if the user is authenticated and in a specific criteria. The two available authentication criterias are:

  1. (default) By using cflogin, cfloginuser, cflogout
  2. Security Validation Object that you create and implement.

Features

  • It secures all events in the system as incoming requests try to access them by wrapping the preProcess execution point.
  • Security rules are created and can be stored in the following mediums, where the interceptor can retrieve them from
    • xml (ColdBox standard)
    • database
    • IoC bean
    • ColdBox Cache (You will place the rules here)
  • A rule engine has been built to provide security for multiple events
  • The rules can be configured to use regular expressions
  • Use CF authentication security
  • Use your own Security Validation by creating a security validation object.

Default Security

This interceptor will try to use ColdFusion's cflogin + cfloginuser authentication by default. However, if you are using your own authentication mechanisms you can still use this interceptor by implementing a Security Validator Object (See Below).

Ex:

<cflogin>
        Your login logic here
        <cfloginuser name="name" password="password" roles="ROLES HERE">
</cflogin>

Security Validator Object

A security validator object is a simple cfc that implements the following function:

userValidator(rule:struct,messagebox:coldbox.system.plugins.messagebox) : boolean

This function must return a boolean variable and it must validate a user according to the rule that just ran by testing the fields that get sent in as a rule. Where this method exists is up to you. The method also receives a reference to the messagebox plugin so you can set messages or anything you like for display purposes if the security fails. Below is a real life example:

<!--- User Validator for security --->
<cffunction name="userValidator" access="public" returntype="boolean" output="false" hint="Verifies that the user is in any permission">
        <!--- ************************************************************* --->
        <cfargument name="rule"         required="true" type="struct"   hint="The rule to verify">
        <cfargument name="messagebox" type="coldbox.system.plugins.messagebox" required="true" hint="The ColdBox messagebox plugin. You can use to set a redirection message"/>
        <!--- ************************************************************* --->
        <!--- Local call to get the user object --->
        <cfset var oUser = getUserSession()>
        <cfset var results = false>
        <cfset var thisPermission = "">
                        
        <!--- Authorized Check, if true, then see if user is valid. --->
        <cfif arguments.rule['authorize_check'] and oUser.getisAuthorized()>
                <cfset results = true>
        </cfif>
        
        <!--- Loop Over Permissions --->
        <cfloop list="#arguments.rule['permissions']#" index="thisPermission">
                <cfif oUser.checkPermission( thisPermission ) >
                        <cfset results = true>
                        <cfbreak>
                </cfif>
        </cfloop>
        
        <!--- Messagebox --->
        <cfif not results>
                <cfset arguments.messagebox.setMessage("warning","You are not authorized to view this page.")>
        </cfif> 
        
        <cfreturn results>
</cffunction>

Declaring the Validator

You have three ways to declare the security validator:

1) This validator object can be set as a property in the interceptor declaration (validator=) as an instantiation path. The interceptor will create it, cache it in itself and try to execute it every time.

<Property name="validator">mypath.model.myvalidator</Property>

2) You can register the validator via the registerValidator() method on this interceptor. This must be called from the application start handler or other interceptors as long as it executes before any preProcess execution occurs, else it will fail validation:

<!--- The following is in my application start handler --->
<cfset getInterceptor('coldbox.system.interceptors.security').registerValidator(myValidator)>

That validator object can exists and come from anywhere you want using the mentioned technique above.

3) Use the validatorIOC property, in which the interceptor grabs the validator from the ioc plugin, whether you are using coldspring or lightwire. It is the bean name

<Property name="validatorIOC">myValidator</Property>

Interceptor Properties

  • useRegex So you can decide to use or not regular expressions on pattern matching
  • useRoutes So you can redirect using the SES routes or normal events
  • rulesSource Where to look for the rules as described above.
  • debugMode Logs all activities as events are checked.
  • validator If set, it must be a valid instantiation path to a security validator object.
Note: If you want to register your own validator instead of the interceptor creating it, please use the registerValidator() method and do not declare the validator property.

Rules

All the security rules follows the following format (However, you can append as much as you like for your own custom validations and the interceptor will register all the columns you pass to it, except from the xml file.):

whitelist varchar A comma delimited list of events or patterns to whitelist or to bypass security on
securelist varchar A comma delimited list of events or patterns to secure
roles varchar A comma delimited list of roles that can access these secure events
permissions varchar A comma delimited list of permissions that can access these secure events
redirect varchar A event or route to redirect if user is not in a role

Sample XML Rules

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- 
Declare as many rule elements as you want, order is important 
Remember that the securelist can contain a list of regular
expression if you want

ex: All events in the user handler
 user\..*
ex: All events
 .*
ex: All events that start with admin
 ^admin

If you are not using regular expression, just write the text
that can be found in an event.
-->
<rules>
    <rule>
        <whitelist>user\.login,user\.logout,^main.*</whitelist>
        <securelist>^user\..*, ^admin</securelist>
        <roles>admin</roles>
        <permissions>read,write</permissions>
        <redirect>user.login</redirect>
    </rule>

    <rule>
        <whitelist></whitelist>
        <securelist>^moderator</securelist>
        <roles>admin,moderator</roles>
        <permissions>read</permissions>
        <redirect>user.login</redirect>
    </rule>
</rules>

Usage

So what can you do with this, really? Well, let's look at some rules using regex, because they are meaner!!

whitelist = ^admin\.(do|dsp)login, ^admin\.logout
securelist = ^admin
roles = admin
redirect = admin.dspLogin

How cool is that, I just secured every single handler that starts with admin, whitelisted any dspLogin,doLogin,logout events. The cool thing is that you can create as many rules as you like.

Unit Testing: MX Unit Integration

A new testing base class has been created to support the MX Unit testing framework. You will find it under coldbox.system.extras.testing.baseMXUnitTest.cfc.

Important: The baseTest.cfc based on CFCUnit has been moved to its own package to allow for the development of more testing classes. You can now find the baseTest.cfc under coldbox.system.extras.testing.baseTest. Please make sure you update your unit tests to reflect this change of location

Cookiestorage Encryption

The cookiestorage plugin has been updated to support encryption on its values. The default is no encryption, but you can activate it by using the following custom settings:

Setting Type Default Value Description
cookiestorage_encryption boolean false Whether to encrypt the values or not
cookiestorage_encryption_seed string (Look in Plugin) The encryption seed to use. Else, use a default one (Not Recommened)
cookiestorage_encryption_algorithm string CFMX_COMPAT & BD_DEFAULT The encryption algorithm to use (According to CFML Engine)

That's it. If you activate encryption, the plugin will take care of encrypting and decrypting values for you.

Renderer Plugin: Render & Cache

Thanks to view caching introduced in 2.5.0 we can now render and cache at the same time. This is the missing piece on rendering and caching views, since before you could only cache views that where set by the event handlers. Now, you can render and cache at the same time.

renderView( view, [cache], [cacheTimeout], [cacheLastAccessTimeout] )

<!--- The following snippet is of a layout file --->
<body>
  <!--- Render the header and cache it for 10 minutes --->
  <div id="header">#renderView(view="tags/header",cache=true,cacheTimeout=10)#</div>

  <div id="body">#renderView()#</div>

  <!--- Render the footer and cache it for 10 minutes --->
  <div id="footer">#renderView(view="tags/footer",cache=true,cacheTimeout=10)#</div>
</body>
Important If view is cached (ex: Footer), then anywhere the footer view will be renderer it will rendered from cache until it expires or you programmatically purge it. So you can share cached views anywhere in your layouts or views.

Flash Persistence Scope: Session or Client

You can now decide where the flash persistence of variables across redirections occurs in. You can choose between:

  • session
  • client

The framework will then switch where it saves it persistence variables. You have a framework-wide setting in the framework's setting.xml but also a setting: FlashURLPersistScope that you can set in your coldbox.xml file to override the framework wide settings.

<Setting name="FlashURLPersistScope" value="session" />

<Setting name="FlashURLPersistScope" value="client" />

This takes effect when you pass in a persistence list form the request collection to the setNextEvent() or setNextRoute() methods. However, there is also a method by itself called persistVariables()

Run Private Events

You can now run private events from anywhere inside your framework life cycle! If you want to create private events, just set their access to private. Then use the runEvent method to execute private events by passing the private argument to true. That's it. Let's say I have a handler named users and have a private action called processRoles, then I would do the following to execute it:

<cfset RunEvent(event="users.processRoles",private=true)>

That is all folks. The advantage of using runEvent is that it goes through the full execution life cycle (Executes interceptors, pre/post process events, and times your execution.). The debugger will even tell you when a private event was executed.

Caching Updates

The ColdBox cache has been really optimized and some great new features have been added to it. The cache has been the foundation of the framework and it is even more solid than ever. It was based on fixed limits, but now it has morphed into the memory sensitive space. Below are the major improvements to the current ColdBox cache, more features will be posted at a later date.

JVM Memory Sensitive

The cache now supports java soft references as their basis for non-eternal objects apart from the fixed limits and algorithms of the cache. The best results can now be achieved with a mixed mode cache, where fixed limits are mixed with memory sensitive algorithms. You can cache without worrying that out of memory errors will occur (Again, theoritically!)

JDK 1.5 > Concurrency Classes

If you are using ColdFusion 8, Bluedragon 7 or Railo 2.X, you will get the benefits of using the JDK 1.5 concurrency collections. The cache object pool has been updated to support concurrent hash maps for atomical operations that will provide even more stability under heavy load and faster access times under heavy load.

Create your own Eviction Policies

A basic interface has been created for you to create and implement your own eviction policies if you so desired. ColdBox comes bundled with LFU and LRU eviction policies.

Cache Panel Updates

The cache panel has been udpated with better charting and more descriptive statistics including how much the JVM has garbage collected your cache references. This is a great way for you to know how the cache is performing and how to tweak it under load.

Discard Mechanism Updates

The clearing and expiring of keys has been updated to take advantage of regular expressions and asynchronous operations of CFML engines that support threading. You can now take advantage of the snippet mechanism to clear or expire objects by just using cache key snippets with or without regular expressions.

cfcViewer plugin updated

The cfcviewer plugin has been totally revamped. It is now even easier to document components. All you do is use the plugin and render it. You can override the styles, links, js links, and even the rendering template and create your own. All the tools necessary to document your applications. The API is really easy to use and direct.

A new method has been created in the request context to help you build links whether you are using the routing system or the event system. The context knows if you are running in SES mode, you can also use event.isSES() to help you deal with routing. The method takes in one arguments, the event or route you want to construct the link to.

<cfform action="#event.buildLink('users/create')#">

</cfform>

Copyright 2006 ColdBox Framework by Luis Majano