What's new with ColdBox 2.6.2
- What's new with ColdBox 2.6.2
- Introduction
- Bug Fixes
- Model Integration
- Prerender Interceptor Receives content to render
- Cache Panel Commands
- New Application Template and Generator
- ColdBox Dashboard 2.2.4 : Open Source
- Pre Handler Exception and Only Lists
- Post Handler Exception and Only Lists
- New Handler Public Property: this.EVENT_CACHE_SUFFIX
- Anti Samy: New XSS plugin
- ColdBox SideBar improvements
- Implicit Views
- New Interceptor: Deploy
- New ${varname} replacement on coldbox.xml
- ioc plugin can now use ${} on Coldspring/Lightwire
- Security Validator Updates
- Unit testing property: this.PERSIST_FRAMEWORK
- New Super Type Methods: locateFilePath(), locateDirectoryPath()
- setNextEvent() now detects SES mode
- New Sample App: ColdBox SES
- New Sample App: Simple Blog
- New param to dump() to abort also
- ColdBox Cache Updates
- New Plugin: ClusterStorage on Code Depot
- Bean Factory Updates
- Autowire any bean from the ioc plugin
- New Autowire attribute overrides
- Implicit View Helpers
- New i18n Locale Storage: Cookie
- Utilities: sendFile()
- New Transfer Extra: Transfer Loader Interceptor
- SES Package Resolver
- 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.
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
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 |
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" />
