What's new with ColdBox 2.6.2

  1. What's new with ColdBox 2.6.2
    1. Introduction
    2. Bug Fixes
    3. Model Integration
      1. Convention Location
      2. External Location
      3. Usage Methods
      4. Dependencies DSL
      5. Constructor & Setter Dependencies
      6. Injecting Dependencies
      7. Model Mappings
      8. Persistence
      9. Simple Example
        1. Coldbox.xml
        2. Model Mappings
        3. User Service
        4. User Gateway
        5. Handler Code
      10. Review
    4. Prerender Interceptor Receives content to render
    5. Cache Panel Commands
    6. New Application Template and Generator
    7. ColdBox Dashboard 2.2.4 : Open Source
    8. Pre Handler Exception and Only Lists
    9. Post Handler Exception and Only Lists
    10. New Handler Public Property: this.EVENT_CACHE_SUFFIX
    11. Anti Samy: New XSS plugin
    12. ColdBox SideBar improvements
    13. Implicit Views
    14. New Interceptor: Deploy
      1. How to set it up?
      2. Interceptor Properties
      3. Deploy Command Object
      4. Overview
    15. New ${varname} replacement on coldbox.xml
    16. ioc plugin can now use ${} on Coldspring/Lightwire
    17. Security Validator Updates
    18. Unit testing property: this.PERSIST_FRAMEWORK
    19. New Super Type Methods: locateFilePath(), locateDirectoryPath()
    20. setNextEvent() now detects SES mode
    21. New Sample App: ColdBox SES
    22. New Sample App: Simple Blog
      1. Simple Blog 1
      2. Simple Blog 2
      3. Simple Blog 3
      4. Simple Blog 4
    23. New param to dump() to abort also
    24. ColdBox Cache Updates
    25. New Plugin: ClusterStorage on Code Depot
    26. Bean Factory Updates
    27. Autowire any bean from the ioc plugin
    28. New Autowire attribute overrides
    29. Implicit View Helpers
    30. New i18n Locale Storage: Cookie
    31. Utilities: sendFile()
    32. New Transfer Extra: Transfer Loader Interceptor
      1. Interceptor Properties
    33. SES Package Resolver
    34. LightWire Collaboration & Updates

Introduction

ColdBox 2.6.2 is a must upgrade maintenance release with some of the most ambitious new enhancements that will blow your socks off. We had these gems safeguarded until they where fully tested and matured. This release might be the last maintenance release on the 2.6 FAITH series, as we move our development gears towards our next major version upgrade of version 2.7. So please enjoy all the fixes and enhancements.

Bug Fixes

Over 32 bug fixes and corrections, that you can see here in our Release Notes.

Model Integration

This features has been long coming and finally it is here. You now have a new convention: ModelsLocation which is by default your model folder in your application root. Now, what does model integration do for you:

  • Easily create and retrieve model objects by using one method: getModel()
  • Easily handle model dependencies by using cfproperty and constructor argument conventions. In other words, we have our own dependency injection framework.
  • A conventions DSL (Domain Specific Language) has been created to facilitiate what needs to be injected in the models
  • Persistence: Use the same rock solid ColdBox cache to persist model objects by using cfcomponent metadata
  • Easily create model mappings or aliases for any model class.
  • Easily populate model objects with data from a request: populateModel()

Wow!! You can do all that? Yes and much more.

Convention Location

All your model objects will be located under your model folder of your application root. This is a convention and can be changed if you so desire by updating the modelsLocation element in the Conventions element of your configuration file. You can also change it for the entire framework installation in the coldbox settings file: coldbox/config/settings.xml.

<Conventions>
        <handlersLocation></handlersLocation>
        <pluginsLocation></pluginsLocation>
        <layoutsLocation></layoutsLocation>
        <viewsLocation></viewsLocation>
        <eventAction></eventAction>     
        <modelsLocation></modelsLocation>       
</Conventions>

External Location

You can also have an external location for your model objects by using a ColdBox setting:

  • ModelsExternalLocation : This setting is the base instantiation path of the model folder.
<Setting name="ModelsExternalLocation" value="coldboxlibrary.models" />

This gives you the ability to create centralized locations for model objects that you can easily bring in to your applications.

Important: Model objects in your conventions take precedence over the external location.

Usage Methods

The following usage methods are available in all handlers, plugins and interceptors. The getModel() method is also available for all unit testing classes and the coldbox proxy.

  • getModel(string name, [boolean useSetterInjection=false], [string onDICompleteUDF=onDIComplete], [boolean debugMode=false])
  • populateModel(any model, [string scope=none], [boolean trustedSetter=false])

The getModel method has 4 arguments:

argument required default description
name true --- The name or alias of the model object
useSetterInjection false false Defaults to false, you can turn it on, to do both setter injection and mixin injection.
onDICompleteUDF false onDIComplete Defaults to onDIComplete. This means that if the object has an onDIComplete() method, it will be called after the object has been created and all dependencies have been injected to it.
debugMode false false Defaults to false, you can turn it on and it will log aout creations and dependencies in your log file.

The populateModel has 3 arguments:

argument required default description
model true --- The name/alias of a model object or an actual instantiated object to populate.
scope false --- If a scope is sent, then the bean factory will populate the variables that match the desired scope name with the request ollection name. Great if you do not want to expose setter methods.
trustedSetter false false Defaults to false, this flag tells the bean factory to call the setter methods without checking if the setter mehod exists. Great for using implicit setters or onMissingMethod setters.
<cfset var oUser = getModel('User')>
<cfset populateModel(oUser)>

or

<cfset var oUser = populateModel("User")>

Dependencies DSL

We have created a nice DSL for dependency injection via cfproperty markers. This is just an extension to what we have already since the 2.0X series started. Not only does the cfproperty types apply to model objects but to anything that is autowired in ColdBox: plugins, handlers, interceptors, ioc produced beans, on demand autowiring and now model objects. Below is a chart of what you can use now in 2.6.2

CFPROPERTY

  • name : The name of the property to be injected
  • type : The type of property to inject (see chart)
  • scope : Into which scope to inject

Type DSL

type Description
ioc Get the named ioc bean and inject it. Name comes from the cfproperty name or argument name
ioc:BeanName Get the ioc bean according to BeanName? in DSL
ocm Get the name key from the !Coldbox cache and inject it. Name comes from the cfproperty name or argument name
ocm:ObjectKey Get the object from the ColdBox cache according to DSL object key.
model Get a model with the same name or alias as defined in the cfproperty name="{name}" attribute. Name comes from the cfproperty name or argument name
model:{name} Same as above but it will get the {name} model object from the DSL and inject it.
model:{name}:{method} Get the {name} model object, call the {method} and inject the results
webservice:{alias} Get a webservice object using an {alias} that matches in your coldbox.xml
coldbox Get the coldbox controller
coldbox:setting:{setting} Get the {setting} setting and inject it
coldbox:plugin:{plugin} Get the {plugin} plugin and inject it
coldbox:myPlugin:{MyPlugin} Get the {MyPlugin} custom plugin and inject it
coldbox:datasource:{alias} Get the datasource bean according to {alias}
coldbox:configBean get the config bean object and inject it
coldbox:mailsettingsbean get the mail settings bean and inject it
coldbox:loaderService get the loader service
coldbox:requestService get the request service
coldbox:debuggerService get the debugger service
coldbox:pluginService get the plugin service
coldbox:handlerService get the handler service
coldbox:interceptorService get the interceptor service
coldbox:cacheManager get the cache manager

Constructor & Setter Dependencies

You can easily use the mentioned DSL to wire up a model object's constructor method init() by placing a marker annotation or additional attribute to the methods arguments. For setter methods, you place the marker in the setter and not the argument. The default attribute is called: _wireme. So a simple example would be the following:

<!--- Constructor Markers --->
<cffunction name="init" returntype="any" output="false">
  <cfargument name="dsn"      type="any" _wireme="coldbox:datasource:myDSN" />
  <cfargument name="transfer" type="transfer.com.Transfer" _wireme="ocm" />
</cffunction>

<!--- Setter Markers --->
<cffunction name="setTransfer" type="transfer.com.Transfer" output="true" _wireme="ocm">

</cffunction>

As you can see, you use the argument or function marker: _wireme to tell the bean factory how to wire up the argument or setter method. Now, if you do not like my default marker, then choose your own. Just create a new setting in your coldbox.xml.cfm named: beanfactory_dslMarker.

<Setting name="beanFactory_dslMarker" value="wireit" />

//Then use the wireit marker:
<cffunction name="init" returntype="any" output="false">
  <cfargument name="dsn" type="any" wireit="coldbox:datasource:myDSN" />
  <cfargument name="transfer" type="transfer.com.Transfer" wireit="ocm" />
</cffunction>

As you can see from the samples above, wiring up the constructor argument is fairly easy and very descriptive. You are also not relying on inherited functionality or conflicting code, it is purely metadata that can be ignored if using another factory other than ColdBox.

Injecting Dependencies

Above we learned how to wire up the arguments of an object's constructor. Below you will learn how to wire up dependencies AFTER the object get's created. So if an object needs dependencies after creation (usually the case), then just use our good old friend cfproperty to demarcate or annotate what needs to be injected. The good thing again, is we just rely on unobtrusive metadata to define what needs to be injected and it can be documented!! Ahh how I love that!

<!--- Autowire Properties --->
<cfproperty name="myMailSettings"       type="ioc"      scope="instance">
<cfproperty name="ColdBox"              type="coldbox"  scope="instance">
<cfproperty name="ModelsPath"           type="coldbox:setting:ModelsPath"       scope="instance">
<cfproperty name="Utilities"            type="coldbox:plugin:Utilities"         scope="instance">
<cfproperty name="ConfigBean"           type="coldbox:configbean"               scope="instance">
<cfproperty name="MailSettingsBean"     type="coldbox:mailsettingsBean"         scope="instance">
<cfproperty name="MySiteDSN"            type="coldbox:datasource:mysite"        scope="instance">
<cfproperty name="testModel"            type="model"                            scope="instance">
<cfproperty name="initDate"             type="model:formBean:getinitDate"       scope="instance">

That is so nice. We can use this DSL to inject almost anything into our model objects. How cool is that! Well, you might be saying, that if I have to give my model a name, what is it. Well it is the path from your model directory to your cfc. Again, you might be saying, what if I refactor, I have to change all the references? Well, the answer is no!! We have model mappings.

Model Mappings

Just by creating a modelMappings.cfm in your config folder and calling a simple method from within it, you can create model object aliases. What does this mean? It means you can create an alias name for your object's instantiation path. This will help hide the true class path that can be so essential when refactoring or changing the location of objects. I highly encourage you to do this:

  • addModelMapping([alias=defaults to the last item in the path],path)
argument type required default description
alias string false last part of the path A comma delimmitted list of aliases to match to a specific path.
path string true --- The instanatiation path of the model object

Remember that the path is the instantiation path from the model folder WITHOUT the model folder and WITHOUT '.cfc'. That's it! Just call this method and create alias names for your model objects. What is also better, is that it is a cfm template, so you can get funky and dynamic. You can do if statements, get data from databases, anything you like.

<cfscript>
        /* Add all the model mappings you want */
        /* addModelMapping(alias="",path="") */
        addModelMapping('MyFormBean','beans.formBean');
        //Adding with a list of aliases
        addModelMapping('SecurityService,Security,MySecurity','security.SecurityService');
</cfscript>

You can also get creative because this is a CF template file and you can even dynamically register all your model objects by doing a directory listing and some creativity. The example above means that I can call the formBean object using the alias or the full path:

  • Alias : getModel('MyFormBean')
  • Full Path : getModel('beans.formBean')

I prefer the alias approach, as I can refactor my model anyway I want, without affecting my controller code.

Persistence

Thanks to metadata and the ColdBox cache, you can use cache metadata attributes and persist your model objects. You can create singletons, transients and even time expired model objects.

<!--- Singleton --->
<cfcomponent name="Model" cache="true" cacheTimeout="0">

<!--- Time Expired Object --->
<cfcomponent name="Model" cache="true" cacheTimeout="40" cacheLastAccessTimeout="15">

<!--- Transient --->
<cfcomponent name="Model">

You can create incredible cache sensitive models now, just by tapping into the ColdBox cache. What is also a plus, is that all model object's metadata are internally cached in a metadata dictionary. So creating model objects are FAST!!!

Simple Example

I will put some simple example code below on how to model a simple User service, gateway, user object and how to use them within a handler.

+ model
  + security
     + UserService.cfc
     + UserGateway.cfc
     + User.cfc

Coldbox.xml

This is a snippet of my configuration file:

<Datasources>
  <Datasource alias="dsn" name="MySite" dbtype="mysql" />
</Datasources>

Model Mappings

I create some mappings so I can refer to them by an alias and not their class path:

addModelMapping('UserService','security.UserService');
addModelMapping('UserGateway','security.UserGateway');

User Service

This user service is a simple cfc that just has a gateway dependency for complex queries:

<!--- Cache of 0 = singleton --->
<cfcomponent name="UserService" output="true" cache="true" cacheTimeout="0">

<!--- Dependencies --->
<cfproperty name="UserGateway" type="model" scope="instance" />
<cfproperty name="SessionStorage" type="coldbox:plugin:sessionstorage" scope="instance" />

<cfscript>
instance = structnew();
</cfscript>

<cffunction name="init" output="false" returntype="UserService">
   <cfreturn this>
</cffunction>

<cffunction name="getAllUsers" output="false" access="public" returntype="query" hint="Returns all users in the database, active and inactive.">
        <!--- ************************************************************* --->
        <cfargument name="orderProperty"        type="string"  required="false" default=""/>
        <cfargument name="orderASC"             type="boolean" required="false" default="true" hint="Order ASC = true, DESC = false"/>
        <!--- ************************************************************* --->
        <cfscript>
                var query = "";
                
                query = instance.UserGateway.findUsers(arguments.orderProperty,arguments.orderASC);
                
                return query;
        </cfscript>
</cffunction>

<cffunction name="authenticateUser" output="false" access="public" returntype="boolean"
                        hint="Authenticate a User. If valid it places them in session. Returns true if user is valid and authenticated and ready for usage.">
        <!--- ************************************************************* --->
        <cfargument name="username" type="string" required="true"/>
        <cfargument name="password" type="string" required="true"/>
        <!--- ************************************************************* --->
        <cfscript>
                /* Prepare results */
                var authenticated = false;
                var oUser = "";

                /* Try to get user by credentials */
                oUser = getUserByCredentials(argumentCollection=arguments);

                //Is User in system.
                if ( oUser.getIsPersisted() ){
                        //Save User State
                        instance.sessionstorage.setVar('CurrentUser', oUser);
                        //Set Return Flags
                        authenticated = true;
                }

                /* Return Results */
                return authenticated;
        </cfscript>
</cffunction>

<!--- Get User By Credentials --->
<cffunction name="getUserByCredentials" output="false" access="public" returntype="User" hint="Returns an active/confirmed user by its credentials">
        <!--- ************************************************************* --->
        <cfargument name="username" type="string" required="true"/>
        <cfargument name="password" type="string" required="true" hint="This argument is hashed internally."/>
        <!--- ************************************************************* --->
        <cfscript>
                var oUser = "";
                var sqlProps = structnew();

                /* prepare sqlProps */
                sqlProps.username = arguments.username;
                sqlProps.password = hash(arguments.password,'SHA-512');
                sqlProps.isConfirmed = 1;
                sqlProps.isActive = 1;
                
                /* Create User */
                oUser = createObject("component","User").init();
                
                /* Try to get user now. */
                instance.userGateway.readByProperties(oUser,sqlProps);
                                        
                return oUser;
        </cfscript>
</cffunction>

<!--- Get A User Session --->
<cffunction name="getUserSession" output="false" access="public" returntype="User"
                        hint="This method checks if a user is in an authorized session, else it returns the default user object.">
        <cfscript>
                var oUser = "";

                //Is user in session
                if ( instance.sessionstorage.exists( 'CurrentUser' ) ){
                        oUser = instance.sessionstorage.getVar( 'CurrentUser' );
                }
                else{
                        oUser = createObject("component","User");
                }

                /* Return User Object */
                return oUser;
        </cfscript>
</cffunction>

<!--- Clean a user's session. --->
<cffunction name="cleanUserSession" output="false" access="public" returntype="void" hint="This method will clean the user session.">
        <cfscript>
                instance.sessionstorage.deleteVar( 'CurrentUser' );
        </cfscript>
</cffunction>

</cfcomponent>

User Gateway

This user gateway is a simple cfc that does complex queries on the database for user operations. I separated it into a gateway object, because I plan to have lots and lots of complex queries for users. If you where doing simple queries or an ORM, maybe just having a service layer would suffice. Again, don't think that everything needs a service-gateway combination and especially 1-1 relationships between tables and objects. Remember that objects must have identity and service layers and manage several tables as long as they provide cohesion and well laid out responsibilities.

<!--- I will just lay out one method not all --->
<!--- Cache of 0 = singleton --->
<cfcomponent name="UserGateway" output="true" cache="true" cacheTimeout="0">

<!--- Dependencies --->
<cfproperty name="dsn" type="coldbox:datasource:dsn" scope="instance" />

<cfscript>
instance = structnew();
</cfscript>

<cffunction name="init" output="false" returntype="UserService">
   <cfreturn this>
</cffunction>

<cffunction name="getAllUsers" output="false" access="public" returntype="query" hint="Returns all users in the database, active and inactive.">
        <!--- ************************************************************* --->
        <cfargument name="orderProperty"        type="string"  required="false" default=""/>
        <cfargument name="orderASC"             type="boolean" required="false" default="true" hint="Order ASC = true, DESC = false"/>
        <!--- ************************************************************* --->
        <cfset var qUser = 0>
        
        <cfquery name="qUser" datasource="#instance.dsn.getName()#">
                select * from users
                order by #arguments.orderProperty# #arguments.orderASC#
        </cfquery>
        
        <cfreturn qUser>
</cffunction>

</cfcomponent>

Handler Code

This is some handler code for a user handler.

<cfcomponent name="User" output="false" extends="coldbox.system.eventhandler" autowire="true">

        <!--- Dependencies --->
        <cfproperty name="UserService" type="Model" scope="instance" />
        
        <cffunction name="list" output="false" returntype="void">
                <cfargument name="event" type="any">
                <cfscript>
                        var rc = event.getCollection();
                        
                        //get all users
                        rc.qUsers = instance.UserService.getAllUsers();
                        
                        //View
                        event.setView('users/list');
                </cfscript>     
        </cffunction>
        
        <cffunction name="login" output="false" returntype="void">
                <cfargument name="event" type="any">
                <cfscript>
                        event.setView("user/login");
                </cfscript>     
        </cffunction>
        
        <cffunction name="doLogin" output="false" returntype="void">
                <cfargument name="event" type="any">
                <cfscript>
                        //Authenticate
                        if( instance.UserService.authenticate(event.getValue("username",""),event.getValue("password","")) ){
                                setNextEvent('user.home');
                        }
                        else{
                                getPlugin("messagebox").setMessage("warning","Username and password not valid. Please try again");
                                setNextEvent('user.login');
                        }
                </cfscript>     
        </cffunction>
        
        <cffunction name="doLogout" output="false" returntype="void">
                <cfargument name="event" type="any">
                <cfscript>
                        instance.UserService.cleanUserSession();
                        setNextEvent('user.login');
                </cfscript>     
        </cffunction>

</cfcomponent>

Review

You can easily create now a nice domain model and easily use it within your application. The new ColdBox model architecture leverages conventions, caching and a new dependency injection mechanisms by using cfproperty markers. Enjoy and start building great domain models!!

Prerender Interceptor Receives content to render

The Prerender interception point now receives the actual rendered content that will be displayed to the user. Now you can create or update your interceptors to manipulate the content to be rendered to the user. You can append data to it, manipulate it, compress it, search and replace, etc.

  • renderedContent : This is the name of the key in the intercepted data structure that will contain the content to render.

Cache Panel Commands

The cache content panel got some nice commands! This will help you issue commands to the cache when you are in debug mode:

  • Clear cache entries
  • View cache entries
  • Clear all cached events
  • Clear all cached views
  • Expire all objects

New Application Template and Generator

The new application template encompasses more usability and a get you started faster motif. We also included a new application generator that will even configure your application for using:

  • ColdBox Sidebar
  • SES via Apache mod_rewrite, ISAPI or even front controller
  • Application.cfc with or without inheritance
  • Exception Handling
  • Asset creations
  • Much More...

The new generated application also looks much nicer and gives you tips on how to start coding:

ColdBox Dashboard 2.2.4 : Open Source

This release now includes the new ColdBox Dashboard version 2.2.4. What is nice about it, is that finally it is OPEN SOURCE!! Enjoy looking through the code, learning and DONATING ME MONEY.

P.S: Yes, it works on Railo and !OpenBD.

Pre Handler Exception and Only Lists

The event handlers get some new public properties to use on its internal interceptor methods.

  • You can now define an exception list of actions for the event handler's preHandler() method by using the this.prehandler_exception property.
  • You can now define an execution only list of actions for the event handler's preHandler() method by using the this.prehandler_only property.
<!--- Execute the prehandler() method only for the private action --->
<cfset this.prehandler_only = "private">

<!--- Do not execute the preHandler() method for the login,logout and dspLogin actions --->
<cfset this.prehandler_exception = "login,logout,dspLogin">

Post Handler Exception and Only Lists

The event handlers get some new public properties to use on its internal interceptor methods.

  • You can now define an exception list of actions for the event handler's postHandler() method by using the this.posthandler_exception property.
  • You can now define an execution only list of actions for the event handler's postHandler() method by using the this.posthandler_only property.
<!--- Execute the posthandler() method only for the private action --->
<cfset this.posthandler_only = "private">

<!--- Do not execute the posthandler() method for the login,logout and dspLogin actions --->
<cfset this.posthandler_exception = "login,logout,dspLogin">

New Handler Public Property: this.EVENT_CACHE_SUFFIX

This new property is great for adding your own dynamic suffixes when using event caching. All you need to do is create a public property called EVENT_CACHE_SUFFIX and populate it with something you want. A great use case for this, is multi-lingual applications. You can set the suffix to the user's locale. Then the event caching mechanisms will automatically append the suffix and thus create event caching according to locales now. How cool is that, a simple key and you have multi-lingual caching at your fingertips.

<!--- Set it to the user's current locale --->
<cfset this.EVENT_CACHE_SUFFIX = getFWLocale()>

Anti Samy: New XSS plugin

Do you want to clean form input or url input from XSS attacks. Now you can with our AntiSamy plugin. AntiSamy is an open source initiative on XSS and it is incredibly good: http://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project. You can now use it very easily in your applications.

<cfset rc.formValue = getPlugin("AntiSamy").HtmlSanitizer(event.getValue("formvalue"))>

That's it, did you expect more!!

ColdBox SideBar improvements

Various bugs and fixes for the sidebar. We added another property: BaseAppPath. On configuration, the interceptor will check for the override. If define, this is the base path for the inclusions. Else, it defaults to cgi.script_name. This is useful when using reverse proxies.

Another new property is the waitTimebeforeOpen in seconds, of how long to wait before opening the sidebar.

Implicit Views

You can now not even define a view if you want, ColdBox will then look for the view according to the executing event. So if the incoming event is called general.home and now view is explicitly defined, ColdBox will look for a view in the general folder called home.cfm.

  • event: general.home
  • view: views/general/home.cfm
Important: If using implicit views, please note that case sensitive OS' cannot be trusted because the event is an incoming variable. So please be aware of this limitation.

New Interceptor: Deploy

This interceptor reads and stores a _deploy.tag file to check for new deployments to a server or server cluster. If the tag has been udpated, the interceptor tells the application to reinitialize itself. This is done via date comparison on the tag. Once the framework starts up, it reads the date timestamp on the tag and saves it on memory as a setting. This deploy interceptor will save you countless pains and mishaps when deploying new builds to a production server or cluster of servers. As each server receives a new request, the deploy tag is compared, if changes are detected, the framework re-initializes the entire application on that specific server and then processing continues with all the new code initialized. What else do you do? Well, maybe get more sleep when doing deploys.

You can use the included ANT script to touch the file with a new timestamp when doing new deploys to your production server or server cluster. Then just make sure you include the file in your deploy.

How to set it up?

  • Place the _deploy.tag and deploy.xml ANT task in your /config directory of your application.
  • Add the Deploy interceptor declaration:
<Interceptor class="coldbox.system.interceptors.deploy">
        <Property name="tagFile">config/_deploy.tag</Property>
        <Property name="deployCommandObject">model.deployCommand</Property>
</Interceptor>

Interceptor Properties

  • tagFile : config/_deploy.tag [required] The location of the tag.
  • deployCommandObject : The class path of the deploy command object to use [optional].

Deploy Command Object

This object is a cfc that must implement an init(controller) method and an execute() method. This command object will be executed before the framework reinit bit is set so you can do any kind of cleanup code or anything you like before the application is re-initialized. Below is a simple example:

<cfcomponent name="DeployCommand" output="false">
        <cffunction name="init" access="public" returntype="any" hint="Constructor" output="false" >
                <cfargument name="controller" required="true" type="coldbox.system.controller" hint="The coldbox controller">
                <cfset instance = structnew()>
                <cfset instance.controller = arguments.controller>
        </cffunction>
        
        <cffunction name="execute" access="public" returntype="void" hint="Execute Command" output="false" >
                <!--- Do your cleanup code or whatever you want here. --->
        </cffunction>
</cfcomponent>

Overview

Maintaining deploys in a production environment of one or more servers can be a drag. You don't want to re-initialize the servers or reboot them if necessary. With this nifty interceptor, all you do is deploy with a marker and the interceptor does the rest. Now you can have more time to play Warcraft (Shhh don't tell the boss)

New ${varname} replacement on coldbox.xml

This is one of my favorite new features of this release. You can now use the ${varname} notation, almost anywhere in the coldbox.xml file to retrieve and create dynamic settings, class paths or anything you like. You can use any setting that can be found in the coldbox.xml.

<!--- Samples --->

<Setting name="MyDSN" value="${datasources.MyAlias.name}" />

<interceptor name="myInterceptor" class="${AppMapping}.model.interceptors.${MyType}Gate" />

As you can see from the samples, you can do multiple ${} notations in one value and you can even use nested values. So go ahead and have some fun!!

ioc plugin can now use ${} on Coldspring/Lightwire

Since we where in the ${} mood, we decided to give you the same abilities on the declaration files for coldspring and lightwire. So now you can use ${} notation ANYWHERE in the file. How cool is that:

<bean id="MyBean" class="${AppMapping}.model.myClass" />

Security Validator Updates

The security validator object can now take in the coldbox controller as an argument to the init method. If there, it will pass it in.

Unit testing property: this.PERSIST_FRAMEWORK

The unit testing routines have been updated to allow the persistence of the framework in application scope as a simulation. Once the test is done, the unit test framework will drop it from scope. However, you can control the persistence by using the following property in your unit test cases:

  • this.PERSIST_FRAMEWORK = boolean (Default is true)
<cfset this.PERSIST_FRAMEWORK = false>

New Super Type Methods: locateFilePath(), locateDirectoryPath()

New methods: locateFilePath(), locateDirectoryPath() for convenience of finding relative or absolute locations within an application. These are great methods for plugin, interceptor and custom configuration methods.

setNextEvent() now detects SES mode

The setNextEvent() method now detects if you are in SES mode and will relocate accordingly. No more switching between setNextEvent() and setNextRoute(), just use setNextEvent() and the framework will relocate for you accordingly. WOHOO!!

New Sample App: ColdBox SES

A new coldbox sample that shows you how to use ses routing.

New Sample App: Simple Blog

This great application was built by Henrik Joreteg and myself. The entire application is a 4 part application that takes you from building a simple application to a more complex and fully OO galore application. Below is how this journey begins:

Simple Blog 1

A basic blog using ColdBox and Transfer. No service layers or gateways. All controller based.

  • Handler Caching setup
  • Usage of the ColdBox Cache
  • Event Caching
  • Event Caching Purging Techiniques
  • Autowiring from the cache
  • SES Routing
  • Basic Request Collection manipulation
  • Multi View Renderings

Simple Blog 2

Refactoring of controller model code to a model layer. Usage of the new ColdBox Model integration and the new Transfer loader interceptor

  • Everything from Version 1 +
  • More advanced event caching
  • Transfer Decorators
  • More SES Routing
  • Model Layer Creation, primitive Service Layer
  • TQL queries
  • CF8 Per-App Mappings
  • Model Integration
  • Transfer loader interceptor

Simple Blog 3

Refactoring the model layer to use more service layer approaches and more usage of ColdBox Goodies. As our application gets more complex and our OO domain model grows, we are starting to see how our dependencies are getting out of hand and we are writing a lot of code for them.

  • New RSS Service with RSS generation
  • Usage of the event.renderData() method.
  • More dependencies in our app init.

Simple Blog 4

We start to use a dependency injection framework in order to leverage our dependencies and code. Our controller code gets a cleanup because of this.

  • IoC framework usage
  • Usage of the ColdBox Transfer Extras classes:
    • Custom Config Factory so only a coldbox.xml is used, and you can declare multiple datasources.
    • Transfer Decorator Injector
  • Custom Plugins
  • More Transfer Decorators
  • Usage of the security interceptor to secure the application
  • Simple Admin Interface
  • Jquery Integration + Effects
  • Service Layer Expanded
  • Transfer Decorator Injections

New param to dump() to abort also

You can now dump-abort in one shot: dump(var,true)

ColdBox Cache Updates

We did some nice load testing and cache updates to make it perform even better under load. The new locking mechanisms are much cleaner and on a per object basis. If you are using ColdFusion 8 or Railo, you will take benefit of using all the new Java's concurrency classes and the performance will sky rocket as load increases. We also updated the cache with several new methods and refactorings:

  • getMulti() To get multiple objects from the cache
  • setMulti() To set multiple objects in the cache
  • clearKeyMulti() To clear multiple objects from the cache
  • getCachedObjectMetadataMulti() To get multiple metadata about cached objects

New Plugin: ClusterStorage on Code Depot

The code depot now has a new Railo only plugin to work with Railo's cluster scope.

Bean Factory Updates

We just saw all the nice updates to integrate with our model objects, but we also did two great updates to our bean population algorithms. We added two new arguments to our populate methods:

  • scope : If a scope is sent, then the factory will not call setters but use mixins to inject the object with properties if they are found in the passed in scope. Wow, no need for setters, just make sure the properties exist in the specified scope and your are golden!!
  • trustedSetters : As we get more dynamic and funky, most of our objects will not have getters or setters. We could be using onMissingMethod() or implict getters and setters (Railo), with this setting turned on to true, the factory will just trust that a setter exists and call it.

Autowire any bean from the ioc plugin

You can now autowire using the ColdBox conventions any object produced from the ioc plugin. All you need to do to make it work, is add the autowire attribute to the components declaration. That's it!!

New Autowire attribute overrides

You can now override two autowiring properties on a per-object basis by creating two new attributes in your cfcomponent tag:

  • autowire_stopRecursion : The class path of when to stop recursion for this particular object.
  • autowire_setterInjection : Whether to use setter injection or disable it.

Implicit View Helpers

This is a nifty little feature that enables you to create nice helper templates on a per-view basis. If the framework detects the helper, it will inject it into the rendering view so you can use methods, properties or whatever. All you need to do is name the helper like so:

  • view: home.cfm
  • helper: homeHelper.cfm

That's it. Just append Helper to the view name and there you go, the framework will use it.

New i18n Locale Storage: Cookie

The i18n plugin can now use the cookie storage scope for a user's locale.

Utilities: sendFile()

The Utilities plugin now has a new method called sendFile() that you can use to send files to the browser. Super easy to use

New Transfer Extra: Transfer Loader Interceptor

The transfer extras have been expanded and a new interceptor has been born called: TransferLoader. This nice interceptor will configure your application to use Transfer and cache it in the coldbox cache. Easily configure your app for Transfer ORM.

Interceptor Properties

property type required default description
datasourceAlias string true N/A The datasource alias name to use
configPath string true N/A The path of the config file
definitionPath string true N/A The definitions path
TransferFactoryCacheKey string false TransferFactory The key used to store the factory as a singleton in the coldbox cache. (case sensitive)
TransferCacheKey string false Transfer The key used to store transfer as a singleton in the coldbox cache. (case sensitive)
TransactionCacheKey string false TransferTransaction The key used to store transfer transaction as a singleton in the coldbox cache. (case sensitive)
TransferFactoryClassPath string false transfer.TransferFactory The class path override of the factory
TransferConfigurationClassPath string false transfer.com.config.Configuration The class path override of the configuration object

Sample:

<Datasources>
        <Datasource alias="blogDSN" name="simpleblog"   dbtype="mssql"  username="" password="" />
</Datasources>

<Interceptors>
<!-- Transfer Loader -->
<Interceptor class="coldbox.system.extras.transfer.TransferLoader">
        <Property name="ConfigPath">/${AppMapping}/config/transfer.xml.cfm</Property>
        <Property name="definitionPath">/${AppMapping}/config/definitions</Property>
        <Property name="datasourceAlias">blogDSN</Property>
</Interceptor>
</Interceptors>

This declaration will create the following objects in the ColdBox cache.

Cache Key Object
TransferFactory The transfer factory object
Transfer The transfer object
TransferTransaction The transfer transaction object
Important : Please note that all cache keys are case SENSITIVE

SES Package Resolver

You can now use full package notation when calling handlers inside of packages. Before this version you had to call them like this: package.handler/action/whatever/whatever. Now you can use full package resolutions: /package/handler/action/whatever/whatever. This is really nice for SEO and makes your links even prettier.

LightWire Collaboration & Updates

I am now collaborating on the LightWire project and we have done TONS of updates to the framework embedded in ColdBox:

  • Full ColdSpring xml compatiblity
    • Non lazy beans: lazy-init
    • Default lazy directive: default=lazy-init
    • Bean Aliases
  • Parent Factories
  • Parent Hierarchies, this differs from coldspring as we can declare ancestry dependencies in child dependencies. This means that a child factory can use the beans from a parent factory, grandparent, grand grand parent and so on. They can also override bean declarations
  • You can load bean definitions using xml, raw xml or xml objects. parseXMLObj(), parseXMLRaw(), and parseXMLConfigFile()
  • Mixin injections for properties and dependencies
  • Not only declare beans for cfc's but also for java classes and even webservices. You can now declare any java class or webservice and wire it up by using the new type attribute: cfc,java or webservice.
  • Java casting via a new attribute: castTo. So you can cast properties in java objects or webservices
  • Incredible performance and metadata dictionaries, this bean factory FLIES!!
  • Programmatic configuration of beans, but we will be adding a new JSON DSL for adding dependencies that will be very clean and concise.
  • If using the coldspring xml file, you can use ${} variables ANYWHERE in the file and even concatenate them.

<bean id="StringBuffer" class="java.lang.StringBuffer" singleton="false" type="java">
  <constructor-arg name="buffer" castTo="int"><value>16000</value></constructor-arg>
</bean>

<bean id="MyWS" class="http://www.myws.com/update.cfc?wsdl" type="webservice" />