What's New With 6.0.0
ColdBox 6.0.0 is a major release for the ColdBox HMVC platform. It has some dramatic new features as we keep pushing for more modern and sustainable approaches to web development. We break down the major areas of development below and you can also find the full release notes per library at the end.

ColdBox 6.x Engine Support
It is also yet another source code reduction due to the dropping of support for the following CFML Engines:
- Adobe ColdFusion 11
- Lucee 4.5
The info-graphic above shows you the supported engines the 6.x platform will support.

ColdBox 'async' package
We have done a tremendous amount of work to expose all the async and parallel programming constructs in ColdBox to the entire framework so developers can leverage them. There is just so much we have done on this release for concurrency, task scheduling, and parallel programming to include in one page. So visit our Async Programming section to start delving into what we are lovingly calling cbFutures!
Thanks to our new futures approach, all major internal libraries (WireBox, CacheBox, LogBox, MVC) will leverage them for different tasks that require asynchronicity and scheduling. You will see a noticeble difference especially in the following areas:
- Cache Reaping: All cache reaping is now done via a scheduled task running on specific frequencies
- File Appenders: It uses an async schedule to stream log data to files instead of blocking operations for sending logs. It will use a logging in-memory queue to stream the log data to the file. So you can potentially send 10000 log events and eventually they will be streamed to disk.
A ColdBox future is used for async/parallel programming where you can register a task or multiple tasks that will execute in a non-blocking approach and trigger dependent computations which could also be asynchronous. This Future object can then be used to monitor the execution of the task and create rich completion/combining pipelines upon the results of such tasks. You can still use a
get()
blocking operation, but that is an over simplistic approach to async programming because you are ultimately blocking to get the result.ColdBox futures are backed by Java's
CompletableFuture
API, so the majority of things will apply as well; even Java developers will feel at home. It will allow you to create rich pipelines for creating multiple Futures, chaining, composing and combining results.// Parallel Executions
async().all(
() => hyper.post( "/somewhere" ),
() => hyper.post( "/somewhereElse" ),
() => hyper.post( "/another" )
).then( (results)=> logResults( results ) );
// Race Conditions, let the fastest dns resolve
var dnsServer = async().any(
() => dns1.resolve(),
() => dns2.resolve()
).get();
// Process an incoming order
async().newFuture( () => orderService.getOrder() )
.then( (order) => enrichOrder( order ) )
.then( (order) => performPayment( order ) )
.thenAsync(
(order) => dispatchOrder( order ),
async().getExecutor( "cpuIntensive" )
)
.then( (order) => sendConfirmation( order ) );
// Combine Futures
var bmi = async().newFuture( () => weightService.getWeight( rc.person ) )
.thenCombine(
async().newFuture( () => heightService.getHeight( rc.person ) ),
( weight, height ) => {
var heightInMeters = arguments.height/100;
return arguments.weight / (heightInMeters * heightInMeters );
}
)
.get();
// Compose Futures with exceptions
async()
.newFuture( () => userService.getOrFail( rc.id ) )
.thenCompose( ( user ) => creditService.getCreditRating( user ) )
.then( (creditRating) => event.getResponse().setData( creditRating ) )
.onException( (ex) => event.getResponse().setError( true ).setMessages( ex.toString() ) );
This new approach to creating async pipelines and parallel processing, will further create extensibility and robustness in your ColdBox applications.
ColdBox apps by default register a
coldbox-tasks
fixed executor (20 threads - IO bound) that is used internally for cleanups, tasks, and schedules. However, any module or application can leverage it for scheduling tasks or workers.asyncManager.getExecutor( "coldbox-tasks" )
CacheBox has been refactored to leverage the async facilities in ColdBox to schedule cache reaps instead of a request initiating the reaps. This brings a lot more stability and consistency to the reaping of caches as they all execute within the new ColdBox
coldbox-tasks
schedule task executor.If you are in CacheBox standalone mode, then the task scheduler will be called
cachebox-tasks
.LogBox has been entirely rewritten in script and a more fluent programming approach. It has also been revamped to leverage the scheduling executors and async programming aspects of our async package. All loggers now sport logging via an async queue and it is completely non-blocking. If you do heavy logging, the performance will be substantial.

The
ModuleService
and all internal ColdBox services have deeper logging constructs and more information logging to understand what happens inside of the core.
Whoops! Exception Template
Thanks to Eric Peterson, we have included Whoops as part of our core exception handling template. All the new application templates come pre-configured with whoops as part of the
development
custom error template.config/Coldbox.cfc
function development() {
coldbox.exceptionEditor = "vscode";
// coldbox.customErrorTemplate = "/coldbox/system/exceptions/BugReport.cfm"; // static bug reports
coldbox.customErrorTemplate = "/coldbox/system/exceptions/Whoops.cfm"; // interactive bug report
}
Warning: Make sure you DO NOT choose this template on production as it can expose code. We do our best to use environment detection to NEVER render it in production, but things can always happen. So always use it within the
development
method.This exception template will help you visualize and navigate your exceptions so you can fix those pesky bugs 🐞. You can even configure it to open the files directly into your favorite IDE using the
coldbox.exceptionEditor
setting:config/ColdBox.cfc
function development() {
coldbox.exceptionEditor = "vscode";
}
Valid Exception Editors are:
vscode
(Default)vscode-insiders
sublime
textmate
emacs
macvim
idea
atom
espresso

RestHandler UML
After many years of adding a base handler and a response object to our application templates, we finally have integrated them into the core so developers can have even more support when building RESTFul services. This new rest handler will provide you with tons of utilities and approaches to make all of your RESTFul services:
- Uniform
- Consistent
- A consistent and extensible response object
- Error handling
- Invalid Route handling
- Much more
New base class
coldbox.system.RestHandler
which you can inherit from or use our new restHandler
annotation. This will give you access to our enhanced API utilities and the native response object via the request context's getResponse()
method.component extends="coldbox.system.RestHandler"{
function index( event, rc, prc ){
event.getResponse()
.setData( "Hello from restful Land" );
}
}
component resthandler{
function index( event, rc, prc ){
event.getResponse()
.setData( "Hello from restful Land" );
}
}
You can now build all of your api’s using the native response object like the rest templates, but now from the core directly. This Rest Handler gives you the following actions out of the box:
Core Actions | Purpose |
aroundHandler() | Wraps all rest actions uniformly to provide consistency and error trapping. |
onError() | An implicit error handler is provided just in case anything explodes in your restful actions. Sends an appropriate 500 error |
onValidationException() | Traps any and makes sure it sends the appropriate 400 response with the invalid data. Useful for using cbValidation |
onEntityNotFoundException() | Traps any or exceptions and makes sure it send an appropriate 404 response. Useful for leveraging cborm or Quick ORM |
onInvalidHTTPMethod() | Traps any invalid HTTP method security exception and sends the appropriate 405 not allowed response |
onMissingAction() | Traps any invalid actions/resource called in your application and sends the appropriate 404 response |
onAuthenticationFailure() | Traps InvalidCredentials exceptions and sends the appropriate 403 invalid credentials response. If you are using cbSecurity it will also verify jwt token expiration and change the error messages accordingly. |
onAuthorizationFailure() | Action that can be used when a user does not have authorization or access to your application or code. Usually you will call this manually or from a security library like cbSecurity or cbGuard. It will send a 401 not authorized response. |
onInvalidRoute() | Action that can be used as a catch all from your router so it can catch all routes that are invalid. It will send a 404 response accordingly. |
onExpectationFailed() | Utility method for when an expectation of the request fails ( e.g. an expected parameter is not provided ). This action is called manually from your own handlers and it will output a 417 response back to the user. |
The
aroundHandler
() provided in the RestHandler
will intercept all rest calls in order to provide consistency and uniformity to all your actions. It will try/catch for major known exceptions, time your requests, add extra output on development and much more. Here are a list of the features available to you:- Exception Handling
- Automatic trapping of the following exceptions:
InvalidCredentials, ValidationException, EntityNotFound, RecordNotFound
- Automatic trapping of other exceptions
- Logging automatically the exception with extra restful metadata
- If in a
development
environment it will respond with much more information necessary for debugging both in the response object and headers
- Development Responses
- If you are in a
development
environment it will set the following headers for you:x-current-route
x-current-routed-url
x-current-routed-namespace
x-current-event
- Global Headers
- The following headers are sent in each request
x-response-time
: The time the request took in CFx-cached-response
: If the request is cached via event caching
The
aroundHandler()
is also smart in detecting the following outputs from a handler:- Handler
return
results - Setting a view or layout to render
- Explicit
renderData()
calls
getResponse()
- Will get you the current
prc.response
object, if the object doesn’t exist, it will create it and set it for you - The core response object can be found here:
coldbox.system.web.context.Response
If you would like to extend or modify the behavior of the core
RestHandler
then you will have to create your own base handler that inherits from it. Then all of your concrete handlers will inherit from your very own handler.// BaseHandler
component extends="coldbox.system.Resthandler"{
// Modify it here
}
// Then make your own handlers extend from it
component extends="BaseHandler"{
}
The response object can be found here:
coldbox.system.web.context.Response
and the rest handler constructs it by calling the request context’s getResponse
() method. The method verifies if there is a prc.response
object and if it exists it returns it, else it creates a new one. So if you would like to use your very own, then just make sure that before the request you place your own response object in the prc
scope.Here is a simple example using a
preProcess()
interceptor. Create a simple interceptor with commandbox e.gcoldbox create interceptor name=MyInterceptor points=preProcess
and add the following method:
function preProcess( event, interceptData, rc, prc ){
prc.response = wirebox.getInstance( "MyResponseObject" );
}
Don't forget to register your interceptor in
config/Coldbox.cfc:
interceptors = [
{
class : "interceptors.MyInterceptor",
name : "MyInterceptor",
properties : {}
}
];
That’s it. Once that response object is in the
prc
scope, ColdBox will utilize it. Just make sure that your custom Response object satisfies the methods in the core one. If you want to modify the output of the response object a good place to do that would be in the getDataPacket()
method of your own MyResponseObject
. Just make sure this method will return a struct
.Our super type now includes a new
when()
method that will allow you to build functional statements within your handlers. Here is the signature of this functional helper:/**
* Functional construct for if statements
*
* @target The boolean evaluator, this can be a boolean value
* @success The closure/lambda to execute if the boolean value is true
* @failure The closure/lambda to execute if the boolean value is false
*
* @return Returns the super type for chaining
*/
function when( required boolean target, required success, failure )
The
target
is a boolean and can be an expression that evaluates to a boolean. If the target
is true, then the success
closure will be executed for you, if not, the failure
closure will be executed.function save( event, rc, prc ){
var oUser = populateModel( "User" );
when( hasAccess(), () => oUser.setRole( rc.role ) );
}

Singleton Renderer
The entire rendering mechanisms have changed in ColdBox 6 and we now support a singleton based approach to view rendering. It still allows for variable safety, but the way renderings in ColdBox 6 are done are orders of magnitude faster than pre ColdBox 6 days. If you are using applications like ContentBox or Preside CMS or applications with tons of
renderView()
calls, your applications will fly now!Thanks to a community pull request you now have the ability to chose the reinit key instead of the default of
fwReinit
. This is useful for security purposes.coldbox.reinitKey = "cbReinit";
Then you can use it in the request:
http://localhost/index.cfm?cbReinit=true
If you are using LogBox in standalone mode, you can now construct it by passing the path to your LogBox configuration file or no path at all and we will construct LogBox with our new default config file to stream logs to the console.
application.logbox = new LogBox();
application.logbox = new LogBox( "config.MyLogBox" );
These methods have been deprecated in favor of our new
announce()
method. We have also deprecated the argument interceptData
in favor of just data.
// New consistent method
announce( state, data );
// Usage
announce( "myCustomEvent", { data = this } );
Have you ever wanted to dynamically listen to events but not create CFC to enclose the method? Well, now you can use the new
listen()
method which accepts a closure/udf so you can listen to ColdBox interceptions. Here is the method signature:/**
* Register a closure listener as an interceptor on a specific point
*
* @target The closure/lambda to register
* @point The interception point to register the listener to
*/
void function listen( required target, required point )
This allows you to easily register dynamic closures/udfs whenever you like so they can listen to events:
// Within a handler/interceptor/layouts/view
listen( function(){
log.info( "executing from closure listener");
}, "preProcess" );
listen( () => log.info( "executing from closure listener"), "preProcess" );
// Within models (Injecting the interceptor service or controller)
controller
.getInterceptorService()
.listen( function(){
log.info( "executing from closure listener");
}, "preProcess" );
We have created a new interception point that is fired before ColdBox is shutdown completely. This can come from a reinit or an application expiration. This is a great place to shutdown custom executors, or messaging queues like RabbitMQ.
function onColdBoxShutdown(){
myRabbitMQChannel.close();
}
We have done several enhancements to the entire routing capabilities in ColdBox apart from several bug fixes.
We have also re-arranged the arguments so you can easily build links with query strings using positional arguments instead of name-value pairs:
string function buildLink(
to,
queryString = "",
boolean translate = true,
boolean ssl,
baseURL = ""
);
<a href="#event.buildLink( 'main.list', 'userid=4' )#">My Link</a>
The request context method
event.buildLink()
has been now added named route support. The event.route()
method was introduced to do named routing with a name
and params
argument. Now, you can also use this approach but via the to
argument in the buildLink()
method by just passing a struct.// Using the route() method
event.route( "contactUs", { id : 3 } )
// Using the buildLink() method and the to struct arg
event.buildLink( { name : "contactUs", params : { id : 3 } } )
You can now add custom metadata to specific route or resourceful routes by using the
meta()
method or the meta
argument. This is simply a struct of name value pairs you can add into the route record.config/Router.cfc
route( "/render/:format" )
.meta( { secure : false } )
.to( "actionRendering.index" );
// Resources
resources(
resource: "photos",
meta : { secure : true }
);
Now, how good is adding the metadata if you can't get it. So you can get the current route's metadata via the new request context method:
getCurrentRouteMeta()
method:if( event.getCurrentRouteMeta().secure ){
// secure it.
}
We have also added the method
getCurrentRouteRecord()
to the request context so you can get the struct definition of the route record of the currently routed route. Below is a sample screenshot of the record:
Route Record
The
toRedirect()
method has been enhanced to accept a closure as the target
of relocation. This closure will received the parsed parameters, the incoming route record and the event object. You can now determine dynamically where the relocation will go.( route, params, event ) => "/new/route"
function( route, params, event ){ return "/new/route"; }
This is great if you need to actually parse the incoming route and do a dynamic relocation.
route( "/old/api/users/:id" )
.toRedirect( ( route, params, event ) => { return "/api/v1/users/#params.id#" } )
Happy Redirecting!
The full release notes per library can be found below. Just click on the library tab and explore their release notes:
ColdBox HMVC
WireBox
CacheBox
LogBox
- [COLDBOX-48] - CacheBox creates multiple reap threads if the initial one take longer to complete than the reap frequency
- [COLDBOX-862] - when passing custom cfml executors to futures it blows up as the native executor is not set
- [COLDBOX-875] - PopulateFromQuery : Gracefully handle out of index rownumber in populateFromQuery #450
- [COLDBOX-851] - All ColdBox apps get a `coldbox-tasks` scheduler executor for internal ColdBox services and scheduled tasks
- [COLDBOX-852] - Updated the default ColdBox config appender to be to console instead of the dummy one
- [COLDBOX-853] - ColdBox controller gets a reference to the AsyncManager and registers a new `[email protected]` wirebox mapping
- [COLDBOX-855] - Allow for the application to declare it's executors via the new `executors` configuration element
- [COLDBOX-856] - Allow for a module to declare it's executors via the new `executors` configuration element
- [COLDBOX-869] - Response, SuperType => New functional if construct when( boolean, success, failure )
- [COLDBOX-871] - Removed fwsetting argument from getSetting() in favor of a new function: getColdBoxSetting()
- [COLDBOX-874] - BaseTestCase new method getHandlerResults() to easy get the handler results, also injected into test request contexts
- [COLDBOX-894] - New listen() super type and interceptor service method to register one-off closures on specific interception points
- [COLDBOX-905] - The buildLink( to ) argument can now be a struct to support named routes : { name, params }
- [COLDBOX-906] - Move queryString as the second argument for buildLink() so you can use it with psoitional params
- [COLDBOX-907] - New context method: getCurrentRouteRecord() which gives you the full routed route record
- [COLDBOX-908] - New context method: getCurrentRouteMeta() which gives you the routed route metadata if any
- [COLDBOX-910] - Every route record can now store a struct of metadata alongside of it using the `meta` key
- [COLDBOX-912] - Allow toRedirect() to accept a closure which receives the matched route, you can process and then return the redirect location
- [COLDBOX-915] - New onColdBoxShutdown interception point fired when the entire framework app is going down
- [COLDBOX-866] - onInvalidEvent is now removed in favor of invalidEventHandler, this was deprecated in 5.x
- [COLDBOX-833] - Improvements to threading for interceptors and logging to avoid dumb Adobe duplicates
- [COLDBOX-841] - Change announceInterception() and processState() to a single method name like: announce()
- [COLDBOX-895] - migrating usage of cgi.http_host to cgi.server_name due to inconsistencies with proxy requests that affects caching and many other features
- [WIREBOX-91] - Injector's get a reference to an asyncManager and a task scheduler whether they are in ColdBox or non-ColdBox mode
- [WIREBOX-94] - getInstance() now accepts either dsl or name via the first argument and initArguments as second argument
- [CACHEBOX-63] - cfthread-20506;variable [ATTRIBUES] doesn't exist;lucee.runtime.exp.ExpressionException: variable [ATTRIBUES] doesn't exist
- [CACHEBOX-60] - CacheFactory gets a reference to an asyncManager and a task scheduler whether they are in ColdBox or non-ColdBox mode
- [LOGBOX-35] - FileAppender: if logging happens in a thread, queue never gets processed and, potentially, you run out of heap space
Last modified 1yr ago