arrow-left

Only this pageAll pages
gitbookPowered by GitBook
triangle-exclamation
Couldn't generate the PDF for 280 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

6.x

Loading...

Intro

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

For Newbies

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

The Basics

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

What's New With 6.10.0

May 13, 2024

hashtag
Release notes - ColdBox Platform - 6.10.0

hashtag
Bug

COLDBOX-1274arrow-up-right javacasting to long for new Java LocalDateTime instead of int, Adobe not doing type promotion

Render encapsulator bleed of this scope by engines

hashtag
Improvement

Removal of deprecated CFML functions in core

Improved engine detection by the CFMLEngine feature class

Remove unsafe evaluate function usage

What's New With 6.11.0

April 28, 2025

hashtag
Release notes - ColdBox Platform - 6.11.0

hashtag
New Feature

BoxLang CacheBox Provider

hashtag
Improvement

Update all cbproxies to work with BoxLang

detect ORM Hibernate Version for BoxLang

hashtag
Bug

Update all GitHub actions to make sure the builds work

populator discover entity name not accouting for missing entityname

Release History

A historical snapshot of all major versions of ColdBox

In this section you will find the release notes for each version we release under this major version. If you are looking for the release notes of previous major versions use the version switcher at the top left of this documentation book. Here is a breakdown of our major version releases.

What's New With 6.9.0

June 9, 2023

hashtag
Added

  • Added debug argument to ScheduleExecutor and Scheduler when creating tasks for consistency

COLDBOX-1279arrow-up-right
COLDBOX-1273arrow-up-right
COLDBOX-1275arrow-up-right
COLDBOX-1278arrow-up-right
COLDBOX-1322arrow-up-right
COLDBOX-1321arrow-up-right
COLDBOX-1323arrow-up-right
COLDBOX-1320arrow-up-right
COLDBOX-1325arrow-up-right

  • Version 3.0 - March 2011

  • Version 2.0 - April 2007

  • Version 1.0 - June 2006

  • Version 6.0 - August 2020
    Version 5.0 - July 2018arrow-up-right
    Reorganized ScheduledTasks functions within the CFC into code groups and comments

    hashtag
    Improvements

    • Scheduled Tasks Updates

    hashtag
    Fixed

    • RestHandler OnError() Exception not checking for empty `exception` blocks which would cause another exception on development ONLY

    COLDBOX-1229arrow-up-right
    Version 4.0 - January 2015arrow-up-right
    COLDBOX-1230arrow-up-right
    COLDBOX-1226arrow-up-right
    COLDBOX-1145arrow-up-right

    What's New With 6.8.1

    August 11, 2022

    hashtag
    Bug

    COLDBOX-1138arrow-up-right Event Cache Response Has Status Code of 0 (or Null)

    COLDBOX-1139arrow-up-right make event caching cache keys lower cased to avoid case issues when clearing keys

    CacheBox

    The CacheBox structure is based on the CacheBox declaration DSLarrow-up-right, and it allows you to customize the caches in your application. Below are the main keys you can fill out, but we recommend you review the CacheBox documentation for further detail.

    Info : We would recommend you create a config/CacheBox.cfc and put all your caching configuration there instead of in the main ColdBox configuration file. This will give you further portability and decoupling.

    hashtag
    ConfigFile

    An absolute or relative path to the CacheBox configuration CFC or XML file to use instead of declaring the rest of the keys in this structure. So if you do not define a cacheBox structure, the framework will look for the default value: config/CacheBox.cfc and it will load it if found. If not found, it will use the default CacheBox configuration found in /coldbox/system/web/config/CacheBox.cfc

    hashtag
    ScopeRegistration

    A structure that enables scope registration of the CacheBox factory in either server, cluster, application or session scope.

    hashtag
    DefaultCache

    The configuration of the default cache which will have an implicit name of default which is a reserved cache name. It also has a default provider of CacheBox which cannot be changed.

    hashtag
    Caches

    A structure where you can create more named caches for usage in your CacheBox factory.

    Settings

    These are custom application settings that you can leverage in your application.

    // Custom Settings
    settings = {
        useSkins = true,
        myCoolArray = [1,2,3,4],
        skinsPath = "views/skins",
        myUtil = createObject("component","#appmapping#.model.util.MyUtility")
    };

    You can read our Using Settings section to discover how to use all the settings in your application.

    Modules

    The modules structure is used to configure the behavior of the ColdBox Modules.

    modules = {
        // Will auto reload the modules in each request. Great for development but can cause some loading/re-loading issues
        autoReload = true,
        // An array of modules to load ONLY
        include = [],
        // An array of modules to EXCLUDE for operation
        exclude = [ "paidModule1", "paidModule2" ]
    };

    Danger Please be very careful when using the autoReload flag as module routing can be impaired and thread consistency will also suffer. This is PURELY a development flag that you can use at your own risk.

    ModuleSettings

    This structure is used to house module configurations. Please refer to each module's documentation on how to create the configuration structures. Usually the keys will match the name of the module to be configured.

    component {
    
         function configure() {
    
             moduleSettings = {
                 myModule = {
                    someSetting = "overridden"
                 }
            };
        }
    }
    //cachebox configuration
    cachebox = {
        // Location of the configuration CFC for CacheBox
        configFile = "config/CacheBox.cfc",
        // Scope registration for CacheBox
        scopeRegistration = {enabled=true,scope=application,key=cacheBox},
        // Default Cache Configuration
        defaultCache  = "views",
        // Caches Configuration
        caches      = {}
    };

    What's New With 6.8.2

    May 1, 2023

    hashtag
    Added

    • Github actions for LTS Releases

    • LTS Updates

    hashtag
    Bugs

    • CFProvider ACF versions are Hard-Coded

    • WireBox caches Singletons even if their autowired dependencies throw exceptions.

    What's New With 6.6.1

    February 17, 2022

    hashtag
    Release Notes

    Bugs

    • Remove debug writedumps left over from previous testing

    • Fix instance of bad route merging the routes but loosing the handler

    Minor Improvements

    • Update Response Pagination Properties for Case-Sensitive Engines

    • default status code to 302 in the internal relocate() just like CFML does instead of 0 and eliminate source

    • Update the internal cfml engine checker to have more engine based feature checkers

    Bugs

    • Bug in JDBCMetadataIndexer sortedKeys() using non-existent variable arguments.objectKey

    Minor Improvements

    InterceptorSettings

    This structure configures the interceptor service in your application.

    //Interceptor Settings
    interceptorSettings = {
        throwOnInvalidStates = false,
        customInterceptionPoints = "onLogin,onWikiTranslation,onAppClose"
    };

    hashtag
    throwOnInvalidStates

    This tells the interceptor service to throw an exception if the state announced for interception is not valid or does not exist. Defaults to false.

    hashtag
    customInterceptionPoints

    This key is a comma delimited list or an array of custom interception points you will be registering for custom announcements in your application. This is the way to provide an observer-observable pattern to your applications.

    Info Please see the section for more information.

    Rendering With Local Variables

    You can pass localized arguments to the renderView() and renderLayout() methods in order to encapsulate the rendering via the args struct argument. Much like how you make method calls with arguments. Inside of your layouts and views you will receive the same args struct reference as well.

    This gives you great DRYness (yes that is a word) when building new and edit forms or views as you can pass distinct arguments to distinguish them and keep structure intact.

    hashtag
    Universal Form

    hashtag
    New Form

    hashtag
    Edit Form

    Introduction

    ColdBox is a conventions-based HMVC web development framework for ColdFusion (CFML).

    hashtag
    ColdBox HMVC Platform- v6.x

    ColdBox is the de-facto enterprise-level HMVC framework for ColdFusion (CFML) developers. It's professionally backed, conventions-based, modular, highly extensible, and productive. Getting started with ColdBox is quick and painless. ColdBox takes the pain out of development by giving you a standardized methodology for modern ColdFusion (CFML) development with features such as:

    Adding A Layout

    Every time the framework renders a view, it will try to leverage the default layout which is located in layouts/Main.cfm by convention. This is an HTML file that gives format to your output and contains the location of where the view you want should be rendered.

    circle-check

    Tip : The request context can also be used to choose a different layout at runtime via the event.setLayout() method or the layout argument in the event.setView()

    Flash

    This directive is how you will configure the for operation. Below are the configuration keys and their defaults:

    Configuration

    Learn how to configure ColdBox according to your needs beyond the conventions.

    In this area we will learn how to configure ColdBox programmatically via the config/ColdBox.cfc file. Most of the configurations in ColdBox are pre-set thanks to it's conventions over configuration approach. So the majority of settings are for fine-grained control, third-party modules and more.

    circle-info

    ColdBox relies on conventions instead of configurations.

    triangle-exclamation

    60 Minute Quick Start

    A 60 minute guide to start working with ColdBox

    This guide has been designed to get you started with ColdBox in fewer than 60 minutes. We will take you by the hand and help you build a RESTFul application in 60 minutes or fewer. After you complete this guide, we encourage you to move on to the and then to the other guides in this book.

    You can find the source code of this quickstart here:

    hashtag
    Requirements

    Conventions

    This element defines custom conventions for your application. By default, the framework has a default set of conventions that you need to adhere too. However, if you would like to implement your own conventions for a specific application, you can use this setting, otherwise do not declare it:

    WireBox

    This configuration structure is used to configure the dependency injection framework embedded in ColdBox.

    binder

    The location of the WireBox configuration binder to use for the application. If empty, we will use the binder in the config folder called by conventions: WireBox.cfc

    singletonReload

    A great flag for development. If enabled, WireBox will flush its singleton objects on every request so you can develop without any headaches of reloading.

    What's New With 6.8.0

    July 23, 2022

    hashtag
    Release Notes

    Next Steps

    Congratulations! Did you finish this guide in less than 60 minutes? If you did please leave us some great feedback below. If you didn't, then please do tell us why, we would love to improve our guides.

    You can now move on to the next level in becoming a ColdBox Guru! Choose your path below:

    Routing Namespaces

    You can create a-la-carte namespaces for URL routes. Namespaces are cool groupings of routes according to a specific URL entry point. So you can say that all URLs that start with /testing will be found in the testing namespace and it will iterate through the namespace routes until it matches one of them.

    Much how modules work, where you have a module entry point, you can create virtual entry point to ANY route by namespacing it. This route can be a module a non-module, package, or whatever you like. You start off by registering the namespace using the addNamespace( pattern, namespace ) method or the fluent route().toNamespaceRouting() method.

    Once you declare the namespace you can use the grouping functionality to declare all the namespace routes or you can use a route().withNamespace()

    Layouts

    The layouts array element is used to define implicit associations between layouts and views/folders, this does not mean that you need to register ALL your layouts. This is a convenience for pairing them, we are in a conventions framework remember.

    Before any renderings occur or lookups, the framework will check this array of associations to try and match in what layout a view should be rendered in. It is also used to create aliases for layouts so you can use aliases in your code instead of the real file name and locations.

    Interceptors

    This is an array of interceptor definitions that you will use to register in your application. The key about this array is that ORDER matters. The interceptors will fire in the order that you register them whenever their interception points are announced, so please watch out for this caveat. Each array element is a structure that describes what interceptor to register.

    Warning : Important: Order of declaration matters! Also, when declaring multiple instances of the same CFC (interceptor), make sure you use the name attribute in order to distinguish them. If not, only one will be registered (the last one declared).

    HTML Base Tag

    The base tag in HTML allows you to tell the browser what is the base URL for assets in your application. This is something that is always missed when using frameworks that enable routing.

    circle-info

    base tag defines the base location for links on a page. Relative links within a document (such as <a href="someplace.html"... or <img src="someimage.jpg"... ) will become relative to the URI specified in the base tag. The base tag must go inside the head element.

    We definitely recommend using this HTML tag reference as it will simplify your asset retrievals.

    Post Advices

    With this interceptor you can intercept local event actions and execute things after the requested action executes. You can do it globally by using the postHandler() method or targeted to a specific action post{actionName}().

    The arguments received by these interceptors are:

    • event : The request context reference

    LayoutSettings

    This structure allows you to define a system-wide default layout and view.

    Hint Please remember that the default layout is Main.cfm

    Rendering External Views

    So what if I want to render a view outside of my application without using the setting explained above? Well, you use the externalView() method.

    circle-info

    If you are using ColdBox 6.4 or older, you will want to use the renderExternalView() method name. In ColdBox 6.5.2+, renderExternalView() was deprecated in favor of the new externalView() method.

    Validation

    ColdBox Core MVC does not have validation built-in but it is implemented via the official core cbValidation module. You can easily install the module in your application via:

    You can find much more information about this module in the following resources:

    • Source:

    COLDBOX-1219arrow-up-right
    WIREBOX-132arrow-up-right

    COLDBOX-1088arrow-up-right Switch isInstance check on renderdata in controller to secondary of $renderdata check to optimize speed

    CACHEBOX-81arrow-up-right JDBCStore Dynamically generate queryExecute options + new config to always include DSN due to ACF issues

    COLDBOX-1093arrow-up-right
    COLDBOX-1085arrow-up-right
    COLDBOX-1095arrow-up-right
    COLDBOX-1091arrow-up-right
    COLDBOX-1089arrow-up-right
    CACHEBOX-80arrow-up-right
    Interceptors
  • Learn about Cachingarrow-up-right

  • Learn about Loggingarrow-up-right

  • Learn about Testingarrow-up-right

  • hashtag
    Getting Help

    If you run into issues or just have questions, please jump on our ColdBox Google Grouparrow-up-right and our Slack teamarrow-up-right and ask away.

    ColdBox is Professional Open Source under the Apache 2.0 license. We'd love to have your help with the product.

    Learn about HMVC via ColdBox Modules
    Learn about Dependency Injectionarrow-up-right
    hashtag
    Bug

    COLDBOX-1134arrow-up-right Router closure responses not marshalling complex content to json

    COLDBOX-1132arrow-up-right New virtual app was always starting up the virtual coldbox app instead of checking if it was running already

    hashtag
    Improvement

    COLDBOX-1131arrow-up-right Updated Missing Action Response Code to 404 instead of 405

    COLDBOX-1127arrow-up-right All core async proxies should send exceptions to the error log

    hashtag
    New Feature

    COLDBOX-1130arrow-up-right New config/ColdBox.cfc global injections: webMapping, coldboxVersion

    COLDBOX-1126arrow-up-right Funnel all out and err logging on a ColdBox Scheduled Task to LogBox

    hashtag
    Task

    COLDBOX-1135arrow-up-right Remove HandlerTestCase as it is no longer in usage.

    v6.8.0
    <h1>#args.type# User</h1>
    <form method="post" action="#args.action#">
    ...
    </form>
    #renderView(view='forms/universal',args={type='new',action='user.create'})#
    // flash scope configuration
    flash = {
        // Available scopes are: session,client,cache,mock or your own class path
        scope = "session",
        // constructor properties for the flash scope implementation
        properties = {},
        // automatically inflate flash data into the RC scope at the beginning of a request
        inflateToRC = true, 
        // automatically inflate flash data into the PRC scope at the beginning of a request
        inflateToPRC = false, 
        // automatically purge flash data for you
        autoPurge = true, 
        // automatically save flash scopes at end of a request and on relocations.
        autoSave = true 
    };
    Flash RAM
    //Conventions
    conventions = {
        handlersLocation = "controllers",
        viewsLocation      = "views",
        layoutsLocation  = "views",
        modelsLocation      = "model",
        modulesLocation  = "modules",
        eventAction      = "index"
    };

    Warning : This operation can cause some thread issues and it is only meant for development. Use at your own risk.

    // wirebox integration
    wirebox = {
        binder = 'config.WireBox',
        singletonReload = true
    };
    WireBoxarrow-up-right
    //Register Layouts
    layouts = [
        { 
            // The alias of a layout
            name="",
            // The layout file
            file="",
            // A list of view names to render within this layout
            views="",
            // A list of regex names to match a view
            folders=""
        }
    
        // Examples
        { name="tester",file="Layout.tester.cfm",views="vwLogin,test",folders="tags,pdf/single"    },
        { name="login",file="Login.cfm",folders="^admin/security"}
    ];
    //Register interceptors as an array, we need order
    interceptors = [
    
        { 
            // The CFC instantiation path
            class="",
            // The alias to register in WireBox, if not defined it uses the name of the CFC
            name="",
            // A struct of data to configure the interceptor with.
            properties={}
        }
    
        { class="mypath.MyInterceptor",
          name="MyInterceptor",
          properties={useSetterInjection=false}
        }
    ];
    //Layout Settings
    layoutSettings = {
        // The default layout to use if NOT defined in a request
        defaultLayout = "Main.cfm",
        // The default view to use to render if NOT defined in a request
        defaultView   = "youForgot.cfm"
    };
    <cfoutput>#externalView(view='/myViewsMapping/tags/footer')#</cfoutput>
    Documentation: https://github.com/coldbox-modules/cbox-validation/wikiarrow-up-right
  • ForgeBox : http://forgebox.io/view/cbvalidationarrow-up-right

  • box install cbvalidation
    https://github.com/coldbox/cbox-validationarrow-up-right

    HTTP Method Spoofing

    Although we have access to all the HTTP verbs, modern browsers still only support GET and POST. With ColdBox and HTTP Method Spoofing, you can take advantage of all the HTTP verbs in your web forms.

    By convention, ColdBox will look for an _method field in the FORM scope. If one exists, the value of this field is used as the HTTP method instead of the method from the execution. For instance, the following block of code would execute with the DELETE action instead of the POST action:

    <cfoutput>
    <form method="POST" action="#event.buildLink( 'posts/#prc.post.getId()#' )#">
        <input type="hidden" name="_method" value="DELETE" />
        <button type="submit">Delete</button>
    </form>
    </cfoutput>

    You can manually add these _method fields yourselves, or you can take advantage of ColdBox's HTML Helper startForm() method. Just pass the method you like, we will take care of the rest:

    <cfoutput>
    #html.startForm( action = "posts.#prc.post.getId()#", method="DELETE" )#
        #html.submitButton( name = "Delete", class = "btn btn-danger" )#
    #html.endForm()#
    </cfoutput>

    LogBox

    The logBox structure is based on the LogBox declaration DSL, see the LogBox Documentationarrow-up-right for much more information.

    //LogBox DSL
    logBox = {
        // The configuration file without fileextension to use for operation, instead of using this structure
        configFile = "config/LogBox", 
        // Appenders
        appenders = {
            appenderName = {
                class="class.to.appender", 
                layout="class.to.layout",
                levelMin=0,
                levelMax=4,
                properties={
                    name  = value,
                    prop2 = value 2
                }
        },
        // Root Logger
        root = {levelMin="FATAL", levelMax="DEBUG", appenders="*"},
        // Granualr Categories
        categories = {
            "coldbox.system" = { levelMin="FATAL", levelMax="INFO", appenders="*"},
            "model.security" = { levelMax="DEBUG", appenders="console"}
        }
        // Implicit categories
        debug  = ["coldbox.system.interceptors"],
        info   = ["model.class", "model2.class2"],
        warn   = ["model.class", "model2.class2"],
        error  = ["model.class", "model2.class2"],
        fatal  = ["model.class", "model2.class2"],
        off    = ["model.class", "model2.class2"]
    };

    Info : If you do not define a logBox DSL structure, the framework will look for the default configuration file config/LogBox.cfc. If it does not find it, then it will use the framework's default logging settings.

    ConfigFile

    You can use a configuration CFC instead of inline configuration by using this setting. The default value is config/LogBox.cfc, so by convention you can just use that location. If no values are defined or no config file exists, the default configuration file is coldbox/system/web/config/LogBox.cfc.

    Routing Groups

    There will be a time where your routes will become very verbose and you would like to group them into logical declarations. These groupings can also help you prefixes repetitive patterns in many routes with a single declarative construct. These needs are met with the group() method in the router.

    function group( struct options={}, body );

    The best way to see how it works is by example:

    route( pattern="/news", target="public.news.index" );
    route( pattern="/news/recent", target="public.news.recent" );
    route( pattern="/news/removed", target="public.news.removed" );
    route( pattern="/news/add/:title", target="public.news.add" );
    route( pattern="/news/delete/:slug", target="public.news.remove" );
    route( pattern="/news/v/:slug", target="public.news.view" );

    As you can see from the routes above, we have lots of repetitive code that we can clean out. So let's look at the same routes but using some nice grouping action.

    group( { pattern="/news", target="public.news." }, function(){
    	route( "/", "index" )
    	.route( "/recent", "recent" )
    	.route( "/removed", "removed" )
    	.route( "/add/:title", "add" )
    	.route( "/delete/:slug", "remove" )
    	.route( "/v/:slug", "view" );
    } );

    The options struct can contain any values that you can use within the closure. Grouping can also be very nice when creating namespaces, which is our next section.

    method.
    circle-check

    Tip : The request context can also be used to render a view with NO layout at all via the event.noLayout() method.

    hashtag
    Layout Code

    This location is identified by the following code: renderView()

    The call to the renderView() method with no arguments tells the framework to render the view that was set using event.setView(). This is called a rendering region. You can use as many rendering regions within layouts or even within views themselves.

    circle-info

    Named Regions: The setView() method even allows you to name these regions and then render them in any layout or other views using the name argument.

    hashtag
    Creating A Layout

    Let's create a new simple layout with two rendering regions. Open up CommandBox and issue the following commands:

    Open the layouts/Funky.cfm layout and let's modify it a bit by adding the footer view as a rendering region.

    If you are use to using cfinclude to reuse templates, think about it the same way. renderview() is a much more powerful cfinclude.

    hashtag
    Using The Layout

    Now, let's open the handler we created before called handlers/hello.cfc and add some code to use our new layout explicitly via adding a layout argument to our setView() call.

    Go execute the event now: http://localhost:{port}/hello/index and you will see the view rendered with the words funky layout and footer view at the bottom. Eureka, you have now created a layout.

    circle-info

    You can also leverage the function event.setLayout( "Funky" ) to change layouts and even concatenate the calls:

    event .setView( "hello/index" ) .setLayout( "Funky" );

    If you make changes to any of the main configuration files you will need to re-initialize your application for the settings to take effect.

    hashtag
    Re-initializing an Application

    Please note that anytime you make any configuration changes or there are things in memory you wish to clear out, you will be using a URL action that will tell the ColdBox Bootstrapper to reinitialize the application. This special URL variable is called fwreinit and can be any value or a specific password you setup in the ColdBox configuration directive.

    You can also use CommandBox CLI to reinit your application if you are using its embedded server:

    // reinit with no password
    index.cfm?fwreinit=1
    
    // reinit with password
    index.cfm?fwreinit=mypass
    combination.
    circle-info

    Hint You can also register multiple URL patterns that point to the same namespace

    addNamespace( pattern="/testing", namespace="test" );
    route( "/testing" ).toNamespaceRouting( "test" );
    
    addNamespace( pattern="/news", namespace="blog" );
    route( "/news" ).toNamespaceRouting( "blog" );
    // Via Grouping
    route( "/news" ).toNamespaceRouting( "blog" )
    	.group( { namespace = "blog" }, function(){
    		route( "/", "blog.index" )
      		.route( "/:year-numeric?/:month-numeric?/:day-numeric?", "blog.archives" );
    	} );
      
    
    // Via Routing DSL
    addNamespace( "/news", "blog" );
      
    route( "/" )
      .withNameSpace( "blog" )
      .to( "blog.index" );
    
    route( "/:year-numeric?/:month-numeric?/:day-numeric?" )
      .withNameSpace( "blog" )
      .to( "blog.archives" );
    
    triangle-exclamation

    Caution If you do not use this tag, then every asset reference must be an absolute URL reference.

    <base href="#event.getHTMLBaseURL()#">
    #renderView(view='forms/universal',args={type='edit',action='user.update'})#
    <div id="maincontent">
    #renderView()#
    </div>
    # Create a Funky layout
    coldbox create layout name="Funky"
    
    # Create a footer
    coldbox create view name="main/footer"
    <h1>funky Layout</h1>
    <cfoutput>#renderView()#</cfoutput>
    
    <hr>
    <cfoutput>#renderView( "main/footer" )#</cfoutput>
    function index( event, rc, prc ){
        // param an incoming variable.
        event.paramValue( "name", "nobody" );
        // set a private variable
        prc.when = dateFormat( now(), "full" );
    
        // set the view to render with our new layout
        event.setView( view="hello/index", layout="Funky" );
    }
    $ coldbox reinit

    Conventions instead of configurationarrow-up-right

  • Modern routing enginearrow-up-right

  • RESTFul API readyarrow-up-right

  • A hierarchical approach to MVC using ColdBox Modulesarrow-up-right

  • Event-driven programmingarrow-up-right

  • Async and Parallel programming constructsarrow-up-right

  • Integration & Unit Testingarrow-up-right

  • Included dependency injectionarrow-up-right

  • Caching engine and APIarrow-up-right

  • Logging enginearrow-up-right

  • An extensive eco-systemarrow-up-right

  • Much More

  • It provides a set of reusable code and tools that can be used to increase your development productivity as well as a development standard for working in team environments.

    hashtag
    Versioning

    ColdBox is maintained under the Semantic Versioningarrow-up-right guidelines as much as possible.Releases will be numbered with the following format:

    And constructed with the following guidelines:

    • Breaking backward compatibility bumps the major (and resets the minor and patch)

    • New additions without breaking backward compatibility bumps the minor (and resets the patch)

    • Bug fixes and misc changes bumps the patch

    hashtag
    License

    The ColdBox Platform is open source and licensed under the Apache 2arrow-up-right License.

    • Copyright by Ortus Solutions, Corp

    • ColdBox, CacheBox, WireBox, LogBox are registered trademarks by Ortus Solutions, Corp

    hashtag
    Discussion & Help

    The Ortus Community is the way to get any type of help for our entire platform and modules: https://community.ortussolutions.comarrow-up-right

    hashtag
    Reporting a Bug

    We all make mistakes from time to time :) So why not let us know about it and help us out. We also love pull requests, so please star us and fork us at: https://github.com/coldbox/coldbox-platformarrow-up-right

    hashtag
    Jira Issue Tracking

    • https://ortussolutions.atlassian.net/browse/COLDBOXarrow-up-right

    • https://ortussolutions.atlassian.net/browse/WIREBOXarrow-up-right

    • https://ortussolutions.atlassian.net/browse/LOGBOXarrow-up-right

    hashtag
    Professional Open Source

    ColdBox is a professional open source software backed by Ortus Solutions, Corparrow-up-right offering services like:

    • Custom Development

    • Professional Support & Mentoring

    • Training

    • Server Tuning

    • Security Hardening

    • Code Reviews

    hashtag
    Resources

    • Official Site: https://www.coldbox.orgarrow-up-right

    • CFCasts Video Training: http://www.cfcasts.comarrow-up-right

    • Source Code: https://github.com/coldbox/coldbox-platformarrow-up-right

    • Bug Tracker:

    • Twitter:

    • Facebook:

    • Vimeo Channel:

    hashtag
    HONOR GOES TO GOD ABOVE ALL

    Because of His grace, this project exists. If you don't like this, then don't read it, it's not for you.

    "Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God." Romans 5:5

    Hierarchical MVC arrow-up-right
    HMVC Framework
    Standalone Libraries

    Please make sure you download and install the latest CommandBox CLIarrow-up-right. We will show you how in the Installing ColdBox section.

  • Grab a cup of coffee or tea

  • Get comfortable

  • hashtag
    Need Help?

    The Ortus Community is the way to get any type of help for our entire platform and modules: https://community.ortussolutions.comarrow-up-right

    Getting Started Guide
    https://github.com/coldbox-samples/60-minute-quickstartarrow-up-right

    action : The action name that was intercepted

  • eventArguments : The struct of extra arguments sent to an action if executed via runEvent()

  • rc : The RC reference

  • prc : The PRC Reference

  • hashtag
    Exceptions & Only Lists

    You can fine tune these interception methods by leveraging two public properties in the handler:

    • this.posthandler_only : A list of actions that the postHandler() action will fire ONLY!

    • this.posthandler_except : A list of actions that the postHandler() action will NOT fire on

    Author

    Author biographies of the ColdBox Platform

    hashtag
    Luis Fernando Majano Lainez

    Luis Majano is a Computer Engineer with over 15 years of software development and systems architecture experience. He was born in San Salvador, El Salvadorarrow-up-right in the late 70’s, during a period of economical instability and civil war. He lived in El Salvador until 1995 and then moved to Miami, Florida where he completed his Bachelors of Science in Computer Engineering at Florida International Universityarrow-up-right

    He is the CEO of Ortus Solutionsarrow-up-right, a consulting firm specializing in web development, ColdFusion (CFML), Java development and all open source professional services under the ColdBox, CommandBox and ContentBox stack. He is the creator of ColdBox, ContentBox, WireBox, MockBox, LogBox and anything “BOX”, and contributes to many open source projects. You can read his blog at

    Luis has a passion for Jesus, tennis, golf, volleyball and anything electronic. Random Author Facts:

    • He played volleyball in the Salvadorean National Team at the tender age of 17

    • The Lord of the Rings and The Hobbit is something he reads every 5 years. (Geek!)

    • His first ever computer was a Texas Instrument TI-86 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)

    Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!

    “Trust in the LORD with all your heart, and do not lean on your own understanding.” – Proverbs 3:5

    hashtag
    Contributors

    hashtag
    Jorge Emilio Reyes Bendeck

    Jorge is an Industrial and Systems Engineer born in El Salvador. After finishing his Bachelor studies at the Monterrey Institute of Technology and Higher Education , Mexico, he went back to his home country where he worked as the COO of. In 2012 he left El Salvador and moved to Switzerland in pursuit of the love of his life. He married her and today he resides in Basel with his lovely wife Marta and their daughter Sofía.

    Jorge started working as project manager and business developer at Ortus Solutions, Corp. in 2013, . At Ortus he fell in love with software development and now enjoys taking part on software development projects and software documentation! He is a fellow Christian who loves to play the guitar, worship and rejoice in the Lord!

    Therefore, if anyone is in Christ, the new creation has come: The old has gone, the new is here! 2 Corinthians 5:17

    hashtag
    Brad Wood

    Brad grew up in southern Missouri where he systematically disassembled every toy he ever owned which occasionally led to unintentional shock therapy (TVs hold charge long after they've been unplugged, you know) After high school he majored in Computer Science with a music minor at (Olathe, KS). Today he lives in Kansas City with his wife and three girls where he still disassembles most of his belongings (including automobiles) just with a slightly higher success rate of putting them back together again.) Brad enjoys church, all sorts of international food, and the great outdoors.

    Brad has been programming CFML for 12+ years and has used every version of CF since 4.5. He first fell in love with ColdFusion as a way to easily connect a database to his website for dynamic pages. Brad blogs at () and likes to work on solder-at-home digital and analog circuits with his daughter as well as building projects with Arduino-based microcontrollers.

    Brad's CommandBox Snake high score is 141.

    What's New With 6.1.0

    ColdBox 6.1.0 is a minor release sporting fixes and a few minor updates to make your coding life easier 😂.

    hashtag
    Bugs

    • [] - RenderLayout throws exception when called multiple times in single request with explicit view

    • [] - Adobe compat for null checks on exception beans

    • [] - Can't disable session management

    hashtag
    New Features

    • [] - Buildlink's queryString can now be a struct and it will be converted to the string equivalent for you

    hashtag
    Improvements

    • [] - Whoops can be slow while dumping out CFC instances

    hashtag
    Bugs

    • [] - builder.toVirtualInheritance(): scoping issues

    hashtag
    Bugs

    • [] - Direct console debugging is left in the AbstractAppender and FileAppender

    Requirements

    Routing is enabled by default in the ColdBox application templates in order to work with URL's like this:

    http://localhost/index.cfm/home/about

    As you can see they still contain the index.cfm in the URL. In order to enable full URL rewrites that eliminates that index.cfm you must have a rewrite enabled webserver like Apache, nginx or IIS or a Java rewrite filter which ships with CommandBox by default.

    http://localhost/home/about

    CommandBox has built in rewrites powered by Tuckey and you can enable a server with rewrites by running:

    triangle-exclamation

    Caution Some J2EE servlet containers do not support the forwarding of SES parameters via the routing template (index.cfm) out of the box. You might need to enable full URL rewriting either through a web server or a J2EE filter.

    hashtag
    Some Resources

    • via .htaccess or configuration files (Free)

    • ISAPI rewrite filter for IIS (Paid)

    • native rewrite filter (Free)

    Pre Advices

    With this interceptor you can intercept local event actions and execute things before the requested action executes. You can do it globally by using the preHandler() method or targeted to a specific action pre{actionName}().

    The arguments received by these interceptors are:

    • event : The request context reference

    • action : The action name that was intercepted

    • eventArguments : The struct of extra arguments sent to an action if executed via runEvent()

    • rc : The RC reference

    • prc : The PRC Reference

    Here are a few options for altering the default event execution:

    • Use event.overrideEvent('myHandler.myAction') to execute a different event than the default.

    • Use event.noExecution() to halt execution of the current event. ONLY works when executed by interceptions before the main event. It will never work in pre/post advices.

    See the documentation for more details.

    hashtag
    Exceptions & Only Lists

    You can fine tune these interception methods by leveraging two public properties in the handler:

    • this.prehandler_only : A list of actions that preHandler() will ONLY fire on

    • this.prehandler_except : A list of actions that preHandler() will NOT fire on

    Building Routable Links

    In your views, layouts and handlers you can use the buildLink method provided by the request context object (event) to build routable links in your application.

    Just pass in the routed URL or event and it will create the appropriate routed URL for you:

    hashtag
    QueryString Struct

    The queryString argument can be a simple query string or a struct that represents the query variables to append.

    hashtag
    Named Routes

    Please note that the to argument can be a simple route path, but it can also be a struct. This struct is for routing to named routes. Even though we recommend to use the route() method instead.

    hashtag
    Inspecting The Current Route

    The request context object (event) also has some handy methods to tell you the name or even the current route that was selected for execution:

    • getCurrentRouteName() - Gives you the name of the current route, if any

    • getCurrentRoute() - Gives you the currently executed route

    • getCurrentRoutedURL()

    Sending Files

    We all need to deliver files to users at one point in time. ColdBox makes it easy to deliver any type of file even binary files via the Request Context's (event) sendFile() method.

    function report( event, rc, prc ){
        var prc.reportFile = reportService.createReport();
        
        event
            .sendFile(
                file = prc.reportFile,
                name = "UserReport.xls",
                deleteFile = true
            )
            .noRender();
    }

    hashtag
    Method Signature

    The API Docs can help you see the entire format of the method: https://apidocs.ortussolutions.com/coldbox/6.6.1/coldbox/system/web/context/RequestContext.html#sendFile()arrow-up-right

    The method signature is as follows:

    circle-info

    Please note that the file argument can be an absolute path or an actual binary file to stream out.

    Pathinfo Providers

    By default, the URL mapping processor will detect routes by looking at the CGI.PATH_INFO variable, but you can override this and provide your own function. This feature can be useful to set flags for each request based on a URL and then clean or parse the URL to a more generic form to allow for simple route declarations. Uses may include internationalization (i18n) and supporting multiple experiences based on devices such as Desktop, Tablet, Mobile and TV.

    To modify the URI used by the Routing Services before route detection occurs simply follow the convention of adding a function called pathInfoProvider() to your application Router (config/Router.cfc).

    The pathInfoProvider() function is responsible for returning the string used to match a route.

    // Example PathInfoProvider for detecting a mobile request
    function PathInfoProvider( event ){
      var rc = event.getCollection();
      var prc = event.getCollection(private=true);
    
      local.URI = CGI.PATH_INFO;
    
      if (reFindNoCase('^/m',local.URI) == 0)
      {
        // Does not look like this could be a mobile request...
        return local.URI;
      }
    
      // Mobile Request? Let's find out.
    
      // If the URI is "/m" it is easy to determine that this is a
      // request for the Mobile Homepage.
      if (len(local.URI) == 2)
      {
        prc.mobile = true;
        // Simply return "/" since they want the mobile homepage
        return "/";
      }
    
      // Only continue with our mobile evaluation if we have a slash after
      // our "/m". Without a slash following the /m the route is something
      // else like coldbox.org/makes/cool/stuff
      if (REFindNoCase('^/m/',local.URI) == 1)
      {
        // Looks like we are mobile!
        prc.mobile = true;
    
        // Remove our "/m/" determination and continue
        // processing for languages...
        local.URI = REReplaceNoCase(local.URI,'^/m/','/');
      }
    
      // The URI starts with an "m" but does not look like
      // a mobile request. So, simply return the URI for normal
      // route detection...
      return local.URI;
    }
    circle-info

    The has another useful example for a pathInfo provider

    Executing Routes

    A part from using runEvent() to execute events, you can also abstract it by using the runRoute() method. This method is fairly similar but with the added benefit of executing a NAMED route instead of the direct event it represents. This gives you the added flexibility of abstracting the direct event and leveraging the named route.

    All the same feature of runEvent() apply to runRoute()

    hashtag
    RunRoute()

    Just like you can create links based on named routes and params, you can execute named routes and params as well internally via runRoute()

    hashtag
    Parameters

    The params argument you pass to the runRoute() method will be translated into event arguments. Therefore they will be passed as arguments to the event the route represents:

    In the example above, the userData named route points to the user.data event.

    hashtag
    Module Routes

    If you want to execute module routes, no problem! Just use our @ or : notation to tell the controller from which module's router we should pick the route from.

    Linking Events Together

    ColdBox provides you with a nice method for generating links between events by leveraging an object called event that is accessible in all of your layouts/views and event handlers. This event object is called behind the scenes the , which models the incoming request and even contains all of your incoming FORM and URL variables in a structure called rc.

    circle-check

    Routing By Convention

    Every router has a default route already defined for you in the application templates, which we refer to as routing by convention:

    The URL pattern in the default route includes two special position placeholders, meaning that the handler and the action will come from the URL. Also note that the :action has a question mark (?), which makes the placeholder optional, meaning it can exist or not from the incoming URL.

    • :handler

    RESTFul Extension Detection

    hashtag
    Usage

    ColdBox allows you to detect incoming extensions from incoming paths automatically for you. This is great for building multi-type responses or to just create virtual extensions for events.

    If an extension is detected in the incoming URL, ColdBox will grab it and store it in the request collection (RC) as the variable format. If there is no extension, then rc.format

    How are events called?

    Events are determined via a special variable that can be sent in via the FORM, URL, or REMOTELY called event. If no event is detected as an incoming variable, the framework will look in the configuration directives for the DefaultEvent and use that instead. If you did not set a DefaultEvent setting then the framework will use the following convention for you: main.index

    circle-check

    About This Book

    Learn about the authors of ColdBox and how to support the project.

    The source code for this book is hosted in GitHub: . You can freely contribute to it and submit pull requests. The contents of this book is copyright by and cannot be altered or reproduced without author's consent. All content is provided "As-Is" and can be freely distributed.

    • The majority of code examples in this book are done in cfscript.

    Getting & Setting Values

    We all need values in our applications. That is why we interact with the in order to place data from our model layer into it so our views can display it, or to retrieve data from a user's request. You will either interact with the event object to get/set values or put/read values directly via the received rc and prc references.

    circle-exclamation

    We would recommend you use the private request collection (prc

    HTTP Method Security

    More often you will find that certain web operations need to be restricted in terms of what HTTP verb is used to access a resource. For example, you do not want form submissions to be done via GET but via POST or PUT operations. HTTP Verb recognition is also essential when building strong RESTFul APIs when security is needed as well.

    hashtag
    Manual Solution

    You can do this manually, but why do the extra coding :)

    Rendering Views

    We have now seen how to set views to be rendered from our handlers. However, we can use some cool methods to render views and layouts on-demand. These methods exist in the Renderer and several facade methods exist in the super type so you can call it from any handler, interceptor, view or layout.

    1. renderView()

    2. renderExternalView()

    Implicit Methods

    Every event handler controller has some implicit methods that if you create them, they come alive. Just like the implicit methods in Application.cfc

    hashtag
    onMissingAction()

    With this convention you can create virtual events that do not even need to be created or exist in a handler. Every time an event requests an action from an event handler and that action does not exist in the handler, the framework will check if an onMissingAction()

    Interception Methods

    There are several simple implicit (Aspect Oriented Programming) interceptor methods, usually referred to as advices, that can be declared in your event handler that the framework will use in order to execute them before/after and around an event as its fired from the current handler.

    This is great for intercepting calls, pre/post processing, localized security, logging, RESTful conventions, and much more. Yes, you got that right, just for you and without all the complicated setup involved! If you declared them, the framework will execute them.

    Relocating

    The framework provides you with the relocate() method that you can use to relocate to other events thanks to the framework super type object, the grand daddy of all things ColdBox.

    circle-info

    Please see the for further investigation of all the goodness of methods you have available.

    triangle-exclamation

    Conventions

    Discover the major conventions of the ColdBox framework

    The core conventions delineate the contract between ColdBox and you for file/directory locations and more. Below is a table of the core conventions:

    hashtag
    Directory/File Conventions

    <major>.<minor>.<patch>
    // executes after any action
    function postHandler( event, rc, prc, action, eventArguments ){
    }
    
    // executes after the list() action ONLY
    function postList( event, rc, prc, action, eventArguments ){
    }
    
    // concrete examples
    function postHandler( event, rc, prc, action, eventArguments ){
        log.info("Finalized executing #action#");
    }
    // only fire for the actions: save(), delete()
    this.posthandler_only = "save,delete";
    // DO NOT fire for the actions: login(), doLogin(), logout()
    this.posthandler_except = "login,doLogin,logout"
    // executes before any action
    function preHandler( event, rc, prc, action, eventArguments ){
    }
    
    // executes before the list() action ONLY
    function preList( event, rc, prc, action, eventArguments ){
    }
    
    // concrete example
    function preHandler( event, rc, prc, action, eventArguments ){
        if( !security.isLoggedIn() ){
            event.overrideEvent( 'security.login' );
            log.info( "Unauthorized accessed detected!", getHTTPRequestData() );
        }
    }
    function preList( event, rc, prc, action, eventArguments ){
        log.info("Starting executing the list action");
    }
    /**
     * Builds links to events or URL Routes
     *
     * @to          The event or route path you want to create the link to
     * @queryString The query string to append which can be a regular query string string, or a struct of name-value pairs
     * @translate   Translate between . to / depending on the SES mode on to and queryString arguments. Defaults to true.
     * @ssl         Turn SSl on/off on URL creation, by default is SSL is enabled, we will use it.
     * @baseURL     If not using SES, you can use this argument to create your own base url apart from the default of index.cfm. Example: https://mysample.com/index.cfm
     */
    string function buildLink(
    	to,
    	queryString       = "",
    	boolean translate = true,
    	boolean ssl,
    	baseURL = ""
    ){
    <a href="#event.buildLink( 'home.about' )#">About</a>
    <a href="#event.buildLink( 'user.edit.id.#user.getID()#' )#">Edit User</a>
    config/Coldbox.cfc
    - Your application configuration object (
    optional
    )
  • config/Router.cfc - Your application URL Router (optional )

  • config/CacheBox.cfc - Your application CacheBox configuration (optional )

  • config/WireBox.cfc - Your application WireBox Configuration (optional )

  • handlers - This holds the app's event handlers (controller layer)

  • layouts - Your HTML layouts (view layer)

  • models - This holds your app's CFCs (model layer)

  • modules - This holds the CommandBox tracked modules

  • modules_app - This holds your app's modules

  • tests - Your test harness including unit and integration testing

  • views - Your HTML views will go here (view layer)

  • hashtag
    Execution Conventions

    ColdBox also has several execution conventions. This means that we have a convention or a default for the event, action and layout to be used if you do not tell it what to use:

    Convention

    Default Value

    Description

    Default Event

    main.index

    The default event to execute when no event is specified

    Default Action

    index()

    The default action to execute in an event handler controller if none is specified

    Default Layout

    layouts/Main.cfm

    The default system layout to use

    [WIREBOX-83arrow-up-right] - When using sandbox security, and using a provider DSL the file existence checks blow up
    COLDBOX-920arrow-up-right
    COLDBOX-921arrow-up-right
    COLDBOX-927arrow-up-right
    COLDBOX-928arrow-up-right
    COLDBOX-922arrow-up-right
    WIREBOX-82arrow-up-right
    LOGBOX-53arrow-up-right
    Rewrite rules section

    nginxarrow-up-right native web server (free)

  • Tuckeyarrow-up-right J2EE rewrite filter (free)

  • Apache mod_rewritearrow-up-right
    Helicon Techarrow-up-right
    IIS 7arrow-up-right
    - Gives you the complete routed URL pattern that matched the route
  • getCurrentRoutedNamespace() - Gives you the current routed namespace, if any

  • /**
     * This method will send a file to the browser or requested HTTP protocol according to arguments.
     * CF11+ Compatibility
     *
     * @file The absolute path to the file or a binary file to send
     * @name The name to send to the browser via content disposition header.  If not provided then the name of the file or a UUID for a binary file will be used
     * @mimeType A valid mime type to use.  If not passed, then we will try to use one according to file type
     * @disposition The browser content disposition (attachment/inline) header
     * @abortAtEnd If true, then this method will do a hard abort, we do not recommend this, prefer the event.noRender() for a graceful abort.
     * @extension Only used for binary files which types are not determined.
     * @deleteFile Delete the file after it has been streamed to the user. Only used if file is not binary.
     */
    function sendFile(
        file="",
        name="",
        mimeType="",
        disposition="attachment",
        boolean abortAtEnd="false",
        extension="",
        boolean deleteFile=false
    )
    // Execute
    runRoute( "userData", { id=4 } )
    Tip: You will use the event object to set views, set layouts, set HTTP headers, read HTTP headers, convert data to other types (json,xml,pdf), and much more.

    hashtag
    Building Links

    The method in the request context that builds links is called: buildLink(). Here are some of the arguments you can use:

    hashtag
    Edit Your View

    Edit the views/virtual/hello.cfm page and wrap the content in a cfoutput and create a link to the main ColdBox event, which by convention is main.index. You can use main.index or just main (Remember that index is the default action)

    This code will generate a link to the main.index event in a search engine safe manner and in SSL detection mode. Go execute the event: http://localhost:{port}/virtual/hello and click on the generated URL, you will now be navigating to the default event /main/index. This technique will also apply to FORM submissions:

    circle-check

    Tip You can visit our API Docs for further information about the event object and the buildLink method: http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/web/context/RequestContext.htmlarrow-up-right.

    For extra credit try to use more of the buildLink arguments.

    hashtag
    URL Structure & Mappings

    ColdBox allows you to manipulate the incoming URL so you can create robust URL strategies especially for RESTFul services. This is all done by convention and you can configure it via the application router: config/Router.cfc for more granular control.

    circle-info

    Out of the box we provide you with convention based routing that maps the URL to modules/folders/handlers and actions.

    route( "/:handler/:action" ).end()

    We have now seen how to execute events via nice URLs. Behind the scenes, ColdBox translates the URL into an executable event string just like if you were using a normal URL string:

    • /main/index -> ?event=main.index

    • /virtual/hello -> ?event=virtual.hello

    • /admin/users/list -> ?event=admin.users.list

    • /handler/action/name/value -> ?event=handler.action&name=value

    • /handler/action/name -> ?event=handler.action&name=

    By convention, any name-value pairs detected after an event variable will be treated as an incoming URL variables. If there is no pair, then the value will be an empty string.

    circle-check

    Tip: By default the ColdBox application templates are using full URL rewrites. If your web server does not support them, then open the config/Router.cfc and change the full rewrites method to false: setFullRewrites( false ).

    request context object
    - The handler to execute (It can include a Package and/or Module reference)
  • :action - The action to relocate to (See the ?, this means that the action is optional)

  • circle-info

    Behind the scenes the router creates two routes due to the optional placeholder in the following order:

    1. route( "/:handler/:action" )

    2. route( "/:handler)

    circle-check

    Tip The :handler parameter allows you to nest module names and handler names. Ex: /module/handler/action

    If no action is passed the default action is index

    This route can handle pretty much all your needs by convention:

    hashtag
    Convention Name-Value Pairs

    Any extra name-value pairs in the remaining URL of a discovered URL pattern will be translated to variables in the request collection (rc) for you automagically.

    circle-check

    Tip: You can turn this feature off by using the valuePairTranslation( false ) modifier in the routing DSL on a route by route basis

    route( "/pattern" ) .to( "users.show" ) .valuePairTranslation( false );

    will not be stored and thus will not exist.

    hashtag
    Configuration

    You can configure the extension detection using the following configuration methods:

    Method

    Description

    setExtensionDetection( boolean )

    By default ColdBox detects URL extensions like json, xml, html, pdf which can allow you to build awesome RESTful web services. Default is true.

    setValidExtensions( list )

    Tell the interceptor what valid extensions your application can listen to. By default it listens to: json, jsont, xml, cfm, cfml, html, htm, rss, pdf

    setThrowOnInvalidExtensions( boolean )

    By default ColdBox does not throw an exception when an invalid extension is detected. If true, then the interceptor will throw a 406 Invalid Requested Format Extension: {extension} exception. Default is false.

    circle-exclamation

    Please note that if you have set to throw exceptions if an invalid extension is detected then a 406 exception will be thrown.

    This solution is great and works, but it is not THAT great. We can do better.

    hashtag
    Allowed Methods Property

    Another feature property on an event handler is called this.allowedMethods. It is a declarative structure that you can use to determine what the allowed HTTP methods are for any action on the event handler.

    If the request action HTTP method is not found in the approved list, it will look for a onInvalidHTTPMethod() on the handler and call it if found. Otherwise ColdBox throws a 405 exception that is uniform across requests.

    circle-info

    You can listen for global invalid HTTP methods using the coldbox.onInvalidHTTPMethodHandler located in your config/ColdBox.cfc.

    circle-info

    If the action is not listed in the structure, then it means that we allow all HTTP methods. Just remember to either use the onError() or onInvalidHTTPMethod() method conventions or an exception handler to deal with the security exceptions.

    hashtag
    Allowed Methods Annotation

    You can tag your event actions with a allowedMethods annotation and add a list of the allowed HTTP verbs as well. This gives you a nice directed ability right at the function level instead of a property. It is also useful when leveraging DocBox documentation as it will show up in the API Docs that are generated.

  • renderLayout()

  • Check out the latest API Docsarrow-up-right for the latest arguments:

    circle-info

    Inline renderings are a great asset for reusing views and doing layout compositions

    hashtag
    Model Rendering

    If you need rendering capabilities in your model layer, we suggest using the following injection DSL:

    This will inject a providerarrow-up-right of a Renderer into your model objects. Remember that renderers are transient objects so you cannot treat them as singletons. The provider is a proxy to the transient object, but you can use it just like the normal object:

    property name="renderer" inject="provider:coldbox:renderer";
    method has been declared. If it has, it will execute it. This is very similar to ColdFusion's
    onMissingMethod()
    but on an event-driven framework.

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

    hashtag
    onError()

    This is a localized error handler for your event handler. If any type of runtime error occurs in an event handler and this method exists, then the framework will call your method so you can process the error first. If the method does not exist, then normal error procedures ensue.

    circle-info

    Please note that compile time errors will not fire this method, only runtime exceptions.

    hashtag
    onInvalidHTTPMethod()

    This method will be called for you if a request is trying to execute an action in your handler without the proper approved HTTP Verb. It will then be your job to determine what to do next:

    function onMissingAction( event, rc, prc, missingAction, eventArguments ){
    
    }
    It is extremely important that you use this method when relocating instead of the native ColdFusion methods as it allows you to gracefully relocate to other events or external URIs. By graceful, we mean it does a lot more behind the scenes like making sure the flash scope is persisted, logging, post processing interceptions can occur and safe relocations.

    So always remember that you relocate via relocate() and if I asked you: "Where in the world does event handlers get this method from?", you need to answer: "From the super typed inheritance".

    /**
    * Relocate user browser requests to other events, URLs, or URIs.
    *
    * @event The name of the event to run, if not passed, then it will use the default event found in your configuration file
    * @URL The full URL you would like to relocate to instead of an event: ex: URL='http://www.google.com'
    * @URI The relative URI you would like to relocate to instead of an event: ex: URI='/mypath/awesome/here'
    * @queryString The query string to append, if needed. If in SES mode it will be translated to convention name value pairs
    * @persist What request collection keys to persist in flash ram
    * @persistStruct A structure key-value pairs to persist in flash ram
    * @addToken Whether to add the tokens or not. Default is false
    * @ssl Whether to relocate in SSL or not
    * @baseURL Use this baseURL instead of the index.cfm that is used by default. You can use this for ssl or any full base url you would like to use. Ex: https://mysite.com/index.cfm
    * @postProcessExempt Do not fire the postProcess interceptors
    * @statusCode The status code to use in the relocation
    */
    void function relocate(
    	event,
    	URL,
    	URI,
    	queryString,
    	persist,
    	struct persistStruct,
    	boolean addToken,
    	boolean ssl,
    	baseURL,
    	boolean postProcessExempt,
    	numeric statusCode
    )
    Super Type CFC Docsarrow-up-right
    server start --rewritesEnable
    <a href="#event.buildLink( 'home.about', "page=2&format=simple" )#">About</a>
    
    <a href="#event.buildLink( 'home.about', { page : 2, format: "simple" } )#">About</a>
    
    event.buildLink( {
        name : "routeName",
        params : { ... }
    } )
    
    event.route( "routeName", { ... } )
    /**
     * Executes internal named routes with or without parameters. If the named route is not found or the route has no event to execute then this method will throw an `InvalidArgumentException`.
     * If you need a route from a module then append the module address: `@moduleName` or prefix it like in run event calls `moduleName:routeName` in order to find the right route.
     * The route params will be passed to events as action arguments much how eventArguments work.
     *
     * @name The name of the route
     * @params The parameters of the route to replace
     * @cache Cached the output of the runnable execution, defaults to false. A unique key will be created according to event string + arguments.
     * @cacheTimeout The time in minutes to cache the results
     * @cacheLastAccessTimeout The time in minutes the results will be removed from cache if idle or requested
     * @cacheSuffix The suffix to add into the cache entry for this event rendering
     * @cacheProvider The provider to cache this event rendering in, defaults to 'template'
     * @prePostExempt If true, pre/post handlers will not be fired. Defaults to false
     *
     * @throws InvalidArgumentException
     */
    any function runRoute(
    	required name,
    	struct params={},
    	boolean cache=false,
    	cacheTimeout="",
    	cacheLastAccessTimeout="",
    	cacheSuffix="",
    	cacheProvider="template",
    	boolean prePostExempt=false
    )
    user.cfc
    component{
    
        property name="userService" inject;
    
        function data( event, rc, prc, id=0 ){
            if( id == 0 )
                return {};
                
            return userService.getData( id );
        }
    }
    # Using @ destination
    runRoute( "userData@user", { id=4 } )
    
    # Using : prefix
    runRoute( "user:userData", { id=4 } )
    /**
     * Builds links to events or URL Routes
     *
     * @to          The event or route path you want to create the link to
     * @queryString The query string to append which can be a regular query string string, or a struct of name-value pairs
     * @translate   Translate between . to / depending on the SES mode on to and queryString arguments. Defaults to true.
     * @ssl         Turn SSl on/off on URL creation, by default is SSL is enabled, we will use it.
     * @baseURL     If not using SES, you can use this argument to create your own base url apart from the default of index.cfm. Example: https://mysample.com/index.cfm
     */
    string function buildLink(
    	to,
    	queryString       = "",
    	boolean translate = true,
    	boolean ssl,
    	baseURL = ""
    ){
    <cfoutput>
        <h1>Hello from ColdBox Land!</h1>
        <p><a href="#event.buildLink( "main" )#">Go home</a></p>
    </cfoutput>
    <form action="#event.buildLink( 'user.save' )#" method="post">
    ...
    </form>
    route( ":handler/:action?").end();
    // Basic Routing
    http://localhost/general -> event=general.index
    http://localhost/general/index -> event=general.index
    
    // If 'admin' is a package/folder in the handlers directory
    http://localhost/admin/general/index -> event=admin.general.index 
    
    // If 'admin' is a module
    http://localhost/admin/general/index -> event=admin:general.index
    http://localhost/general/show/page/2/name/luis
    # translate to
    event=general.show, rc.page=2, rc.name=luis
    
    http://localhost/users/show/export/pdf/name
    # translate to
    event=users.show, rc.export=pdf, rc.name={empty value}
    http://localhost/users
    http://localhost/users.json
    http://localhost/users.xml
    http://localhost/users.pdf
    http://localhost/users.html    
    http://localhost/users => rc.format is null, does not exist
    http://localhost/users.json => rc.format = json
    http://localhost/users.xml => rc.format = xml
    http://localhost/users.pdf => rc.format = pdf
    http://localhost/users.html => rc.format = html
    function delete(event,rc,prc){
        // determine incoming http method
        if( event.getHTTPMethod() == "GET" ){
            flash.put("notice","invalid action");
            relocate("users.list");
        }
        else{
            // do delete here.
        }
    }
    this.allowedMethods = {
        actionName : "List of approved HTTP Verbs"
    };
    component{
    
        this.allowedMethods = {
            delete : "POST,DELETE",
            list   : "GET"
        };
    
        function list(event,rc,prc){
            // list only
        }
    
        function delete(event,rc,prc){
            // do delete here.
        }
    }
    function index( event, rc, prc) allowedMethods="GET,POST"{
        // my code here
    }
    <cfoutput>
    // render inline
    #renderView( 'tags/metadata' )#
    
    // render from a module
    #renderView( view="security/user", module="security" )#
    
    // render and cache
    #renderView(
        view="tags/longRendering", 
        cache=true, 
        cacheTimeout=5, 
        cacheProvider="couchbase"
    )#
    
    // render a view from the handler action
    function showData(event,rc,prc){
        // data here
        return renderView( "general/showData" );    
    }
    
    // render an email body content in an email template layout
    body = renderlayout( layout='email',view='templates/email_generic' );
    </cfoutput>
    function sendEmail(){
        
        // code here.
        var body = renderer.renderView( "templates/email" );
    
    }
    // On Error
    function onError( event, rc, prc, faultAction, exception, eventArguments ){
        // prepare a data packet
        var data = {
            error = true,
            messages = exception.message & exception.detail,
            data = ""
        }
    
        // log via the log variable already prepared by ColdBox
        log.error("Exception when executing #arguments.faultAction# #data.messages#", exception);    
    
        // render out a json packet according to specs status codes and messages
        event.renderData(data=data,type="json",statusCode=500,statusMessage="Error ocurred");
    
    }
    function onInvalidHTTPMethod( faultAction, event, rc, prc ){
        return "Go away!";
    }
    relocate( "home" );
    relocate( event="shop", ssl=true );
    relocate( event="user.view", queryString="id=#rc.id#" );
    relocate( url="http://www.google.com" );
    relocate( uri="/docs/index.html" )

    He has a geek love for circuits, micro-controllers and overall embedded systems.

  • He has of late (during old age) become a fan of organic gardening.

  • www.luismajano.comarrow-up-right
    ITESMarrow-up-right
    Industrias Bendek S.A.arrow-up-right
    MidAmerica Nazarene Universityarrow-up-right
    http://www.codersrevolution.comarrow-up-right
    Hint : You can even change the event variable name by updating the EventName setting in your coldbox configuration directive.

    Please note that ColdBox supports both normal variable routing and URL mapping routing, usually referred to as pretty URLs.

    hashtag
    Event Syntax

    In order to call them you will use the following event syntax notation format:

    • no event : Default event by convention is main.index

    • event={handler} : Default action method by convention is index()

    • event={handler}.{action} : Explicit handler + action method

    • event={package}.{handler}.{action} : Packaged notation

    • event={module}:{package}.{handler}.{action} : Module Notation (See )

    This looks very similar to a Java or CFC method call, example: String.getLength(), but without the parentheses. Once the event variable is set and detected by the framework, the framework will tokenize the event string to retrieve the CFC and action call to validate it against the internal registry of registered events. It then continues to instantiate the event handler CFC or retrieve it from cache, finally executing the event handler's action method.

    Examples

    The majority of code generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL - http://www.ortussolutions.com/products/commandboxarrow-up-right
  • All ColdFusion examples designed to run on the open source Lucee Platform or Adobe ColdFusion 11+

  • hashtag
    External Trademarks & Copyrights

    Flash, Flex, ColdFusion, and Adobe are registered trademarks and copyrights of Adobe Systems, Inc.

    ColdBox, CommandBox, FORGEBOX, TestBox, ContentBox, Ortus Solutions are all trademarks and copyrights of Ortus Solutions, Corp.

    hashtag
    Notice of Liability

    The information in this book is distributed “as is”, without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity with respect to loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software and resources described in it.

    hashtag
    Contributing

    We highly encourage contribution to this book and our open source software. The source code for this book can be found in our GitHub repositoryarrow-up-right where you can submit pull requests.

    hashtag
    Charitable Proceeds

    10% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - https://www.harvesting.org/arrow-up-right. So please donate and purchase the printed version of this book, every book sold can help a child for almost 2 months.

    hashtag
    Shalom Children's Home

    Shalom Children's Home

    Shalom Children’s Home (https://www.harvesting.org/arrow-up-right) is one of the ministries that is dear to our hearts located in El Salvador. During the 12 year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 children in 1982. Little by little, more children came on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.

    Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education and life skills training in a Christian environment. The home is supported by a child sponsorship program.

    We have personally supported Shalom for over 6 years now; it is a place of blessing for many children in El Salvador that either have no families or have been abandoned. This is good earth to seed and plant.

    https://github.com/ortus-docs/coldbox-docsarrow-up-right
    Ortus Solutions, Corparrow-up-right
    ) for setting manual data and using the standard request collection (
    rc
    ) for reading the user's unsafe request variables. This way a clear distinction can be made on what was sent from the user and what was set by your code.
    triangle-exclamation

    Important The most important paradigm shift from procedural to an MVC framework is that you NO LONGER will be talking to URL, FORM, REQUEST or any ColdFusion scope from within your handlers, layouts, and views. The request collection already has URL, FORM, and REQUEST scope capabilities, so leverage it.

    request context
    RC/PRC Data Super Highway

    Executes before any requested action (In the same handler CFC)

    pre{action}()

    Executes before the {action} requested ONLY

    postHandler()

    Executes after any requested action (In the same handler CFC)

    post{action}()

    Executes after the {action} requested ONLY

    aroundHandler()

    Executes around any request action (In the same handler CFC)

    around{action}()

    Executes around the {action} requested ONLY

    Interceptor Method

    Description

    preHandler()

    AOParrow-up-right
    Aspect Oriented Programmingarrow-up-right
    RequestContext
    https://ortussolutions.atlassian.net/browse/CACHEBOXarrow-up-right
    Much Morearrow-up-right
    https://ortussolutions.atlassian.net/browse/COLDBOXarrow-up-right
    @coldboxarrow-up-right
    https://www.facebook.com/coldboxplatformarrow-up-right
    https://vimeo.com/channels/coldboxarrow-up-right

    What's New With 6.3.0

    ColdBox 6.3.0 is a minor release that squashes lots of bugs and does tons of improvements for performance!

    hashtag
    Bug

    • [] - Renderer methods assume the module exists and throws exception when sending invalid url data

    • [] - Can no longer have duplicate routes with different conditions

    • [] - Colon (:) in URL Path Causes Exception Error

    • [] - invalidEventHandler does not work when calling invalid action on valid handler

    • [] - autowire annotation for test cases is not working as it should

    • [] - Fix declaring multiple resources at once

    • [] - AsyncManager threads don't release DB connections to pool for Adobe CF

    hashtag
    New Feature

    • [] - Add new exception type catch for the RestHandler: `PermissionDenied` to trap in valid authorizations

    hashtag
    Improvement

    • [] - Content type http header bypasses requestContext with render data - set explicit http header via request context

    • [] - Implement caching strategy for application helper lookups into the `template` cache

    • [] - Coldbox DataMarshaller Throws Error with Lucee-Light Engine

    hashtag
    Bug

    • [] - getStoreMetadataReport() - wrong order of the reduce() parameters

    hashtag
    Improvement

    • [] - Refactor the way cffeed is used so that ACF 2021 doesn't choke on first startups, only when used

    Routing

    ColdBox supports a Routing Service that will provide you with robust URL mappings for building expressive applications and RESTFul services. By convention URL routing will allow you to create URL's without using verbose parameter delimiters like ?event=this.that&m1=val and execute ColdBox events.

    circle-info

    If you are leveraging CommandBox as your server, then full URL rewrites are enabled by default. This means you do not need a web server to remove the index.cfm from the URL.

    hashtag
    What is a route?

    A route is a declared URL pattern that if matched it will translate the URL into one of the following:

    • A ColdBox event to execute

    • A View/Layout to render

    • A Reponse function to execute

    It will also inspect the URL for placeholders and translate them into the incoming Request Collection variables (RC).

    Examples

    hashtag
    Routing Benefits

    There are several benefits that you will get by using our routing system:

    • Complete control of how URL's are built and managed

    • Ability to create or build URLs' dynamically

    • Technology hiding

    hashtag
    Route Visualizer

    As you create route-heavy applications visualizing the routes will be challenging especially for HMVC apps with lots of modules. Just install our and you will be able to visually see, test and debug all your routing needs.

    Executing Events

    Apart from executing events from the URL/FORM or Remote interfaces, you can also execute events internally, either public or private from within your event handlers or from interceptors, other handlers, layouts or views.

    hashtag
    RunEvent()

    You do this by using the runEvent() method which is inherited from our FrameworkSuperType class. Here is the method signature:

    The interesting aspect of internal executions is that all the same rules apply, so your handlers can return content like widgets, views, or even data. Also, the eventArguments enables you to pass arguments to the method just like method calls:

    hashtag
    Executions

    hashtag
    Declaration

    hashtag
    Caching

    As you can see from the function signature you can tell ColdBox to cache the result of the event call. All of the cached content will go into the template cache by default unless you use the cacheProvider argument. The cache keys are also based on the name of the event and the signature of the eventArguments structure. Meaning, the framework can cache multiple permutations of the same event call as long as the eventArguments are different.

    circle-check

    Tip: You can disable event caching by using the coldbox.eventCaching directive in your config/ColdBox.cfc

    Setting Views

    hashtag
    Views (Default Layout)

    The event object is the object that will let you set the views that you want to render, so please explore its API in the CFC Docs. To quickly set a view to render, do the following:

    The view name is the name of the template in the views directory without appending the .cfm. If the view is inside another directory, you would do this:

    The views you set will use the default layout defined in your configuration file which by default is the layouts/Main.cfm

    circle-info

    We recommend that you set your views following the naming convention of your event. If your event is users.index, your view should be users/index. This will go a long way with maintainability and consistency and also will activate implicit views where you don't even have to use the set view method call.

    hashtag
    View With Custom Layouts

    You can also use the setView(), setLayout() methods to tell the framework which view and layout combination to use:

    hashtag
    Views With No Layout

    You can also tell the framework to set a view for rendering by itself with no layout using the noLayout argument

    hashtag
    setView() Arguments

    Here are the arguments for the setView() method:

    hashtag
    Cached Views

    You can leverage the caching arguments in the setView() method in order to render and cache the output of the views once the framework renders it. These cached views will be stored in the template cache region, which you can retrieve or purge by talking to it: getCache( 'template' ).

    hashtag
    View Arguments

    Data can be passed from your handler to the view via rc or prc. If you want to pass data to a view without polluting rc and prc, you can pass it directly via the args parameter, much like a method call.

    Access the data in the view like so:

    hashtag
    No Rendering

    If you don't want to, you don't have to. The framework gives you a method in the event object that you can use if this specific request should just terminate gracefully and not render anything at all. All you need to do is use the event object to call on the noRender() method and it will present to the user a lovely white page of death.

    RESTFul Data

    Out of the box, ColdBox gives you all the RESTFul capabilities you will need to create robust and scalable RESTFul services. Let's add some RESTFul capabilities to our contact listing we created in the previous section.

    circle-check

    Tip: You can find much more information about building ColdBox RESTFul services in our full docs.

    hashtag
    Producing JSON

    If you know beforehand what type of format you will be responding with, you can leverage ColdBox auto-marshalling in your handlers. By default, ColdBox detects any return value from handlers and if they are complex it will convert them to JSON automatically for you:

    ColdBox detects the array and automatically serializes it to JSON. Easy Peasy!

    hashtag
    renderData()

    The request context object has a special function called renderData() that can take any type of data and marshall (convert) it for you to other formats like xml, json, wddx, pdf, text, html or your own type.

    circle-check

    Tip: You can find more information at the API Docs for renderData() here )

    So let's open the handlers/contacts.cfc and add to our current code:

    We have added the following line:

    This tells ColdBox to render the contacts data in 4 formats: xml, json, pdf and html. WOW! So how would you trigger each format? Via the URL of course.

    hashtag
    Format Detection

    ColdBox has the ability to detect formats via URL extensions or an incoming Accepts header. If no extension is sent, then ColdBox attempts to determine the format by inspecting the Accepts header. If we still can't figure out what format to choose, the default of html is selected for you.

    circle-check

    Tip: You can also avoid the extension and pass a URL argument called format with the correct format type: ?format=json.

    hashtag
    Routing

    Let's add a new route to our system that is more RESTFul than /contacts/index.json. You will do so by leveraging the application's router found at config/Router.cfc. Find the configure() method and let's add a new route:

    The route() method allows you to register new URL patterns in your application and immediately route them to a target event. You can even give it a human readable name that can be later referenced in the buildLink() method.

    triangle-exclamation

    Make sure you add routes above the default ColdBox route. If not, your route will never fire.

    We have now created a new URL route called /api/contacts that if detected will execute the contacts.index event. Now reinit the application, why, well we changed the application router and we need the changes to take effect.

    circle-check

    Tip: Every time you add new routes make sure you reinit the application: http://localhost:{port}/?fwreinit.

    You can now visit the new URL pattern and you have successfully built a RESTFul API for your contacts.

    circle-info

    You can find much more about routing in our

    Environments

    The configuration CFC has embedded environment control and detection built-in. Environments can be detected by:

    • regex matching against cgi.http_host

    • detection of an environmental variable called ENVIRONMENT ( Coldbox 5.2 and higher )

    • usage of a detectEnvironment() function

    The first option (regex matching) is the easiest to use, but not very reliable if you are using multiple hostnames or commandbox for re-initialization.

    circle-exclamation

    If you are using commandbox please read ALL options below

    hashtag
    Default: Regex matching against cgi.http_host

    To detect your environments you will setup a structure called environments in your coldbox configuration with the named environments and their associated regular expressions for its cgi host names to match for you automatically. If the framework matches the regex with the associated cgi.http_host, it will set a setting called Environment in your configuration settings and look for that environment setting name in your CFC as a method by convention. That's right, it will check if your CFC has a method with the same name as the environment and if it exists, it will call it for you. Here is where you basically override, remove, or add any settings according to your environment.

    Warning : The environment detection occurs AFTER the configure() method is called. Therefore, whatever settings or configurations you have on the configure() method will be stored first, treat those as Production settings.

    The regex match will also create a global setting called "environment" which you can access and use like this:

    In the above example, I declare a development key with a value list of regular expressions. If I am in a host that starts with cf2016, this will match and set the environment setting equal to development. It will then look for a development method in this CFC and execute it.

    hashtag
    Detection of an environmental variable called ENVIRONMENT

    If you are using environmental variables for your different environments, you can specify an environmental variable called ENVIRONMENT and name it staging, development, testing etcetera, depending on the required environment. As in the regex example, a function named after your environment (e.g. staging() or development() ) will be called after your configure method.

    circle-info

    This method is more reliable than relying on cgi.http_host, since it will never change once configured correctly.

    hashtag
    Custom Environment Detection

    If you are NOT using environmental variables you can use your own detection algorithm instead of looking at the cgi.http_host variable. You will NOT fill out an environments structure but actually create a method with the following signature:

    This method will be executed for you at startup and it must return the name of the environment the application is on. You can check for any condition which distinguishes your environment from your other environments. As long as you return an environment name based on your own logic it will then store it and execute the method if it exists.

    System Settings (Java Properties and Environment Variables)

    ColdBox makes it easy to access the configuration stored in your Java system properties and your server's environment variables, even if you don't know which one it is in! Three methods are provided for your convenience:

    Name

    Arguments

    Description

    getSystemSetting

    ( key, defaultValue )

    Looks for key in properties first, env second. Returns the defaultValue if neither exist.

    getSystemProperty

    ( key, defaultValue )

    Returns the Java System property for key. Returns the defaultValue if it does not exist.

    hashtag
    Accessing System Settings in config/ColdBox.cfc or a ModuleConfig.cfc

    If you are inside config/ColdBox.cfc or a ModuleConfig.cfc or a config/WireBox.cfc you can use the three system settings functions directly! No additional work required.

    hashtag
    Accessing System Settings in Application.cfc

    If you would like to access these methods in your Application.cfc, create an instance of coldbox.system.core.util.Util and access them off of that component. This is required when adding a datasource from environment variables.

    Example:

    hashtag
    Accessing System Settings in other files

    If you need to access these configuration values in other components, consider adding the values to your and injecting the values into your other components

    Upgrading to ColdBox 6

    The major compatibility issues will be covered as well as how to smoothly upgrade to this release from previous ColdBox versions. You can also check out the guide to give you a full overview of the changes.

    hashtag
    Lucee 4.5 Support Dropped

    Lucee 4.5 support has been dropped.

    Using Settings

    The ColdBox Controller (stored in ColdFusion application scope as application.cbController) stores all your application settings and also your system settings:

    • ColdboxSettings : Framework specific system settings

    My First Handler & View

    hashtag
    Handler Scaffolding

    Now let's create our first controller, which in ColdBox is called Event Handler. Let's go to CommandBox again:

    This will generate the following files:

    Named Routes

    You can register routes in ColdBox with a human friendly name so you can reference them later for link generation and more.

    hashtag
    Registering Named Routes

    You will do this in two forms:

    Pattern Placeholders

    hashtag
    Alphanumeric Placeholders

    In your URL pattern you can also use the : syntax to denote a variable placeholder. These position holders are alphanumeric by default:

    Once a URL is matched to the route above, the placeholders (:year/:month?/:day?) will become request collection (RC

    ColdBox.cfc

    The ColdBox.cfc is the main applications' configuration object.

    The ColdBox configuration CFC is the heart of your ColdBox application. It contains the initialization variables for your application and extra information used by third-party modules and ultimately how your application boots up. In itself, it is also an event listener or , so it can listen to life-cycle events of your application.

    This CFC is instantiated by ColdBox and decorated at runtime so you can take advantage of some dependencies. Here is a table of the automatic injection this object has:

    Layouts & Views

    ColdBox provides you with a very simple but flexible and powerful layout manager and content renderer. You no longer need to create module tags or convoluted broken up HTML anymore. You can concentrate on the big picture and create as many as your application needs. Then you can programmatically change rendering schemas (or skinning) and also create composite or component based views.

    In this section we will explore the different rendering mechanisms that ColdBox offers and also how to utilize them. As you know, are our controller layer in ColdBox and we will explore how these objects can interact with the user in order to render content, whether HTML, JSON, XML or any type of rendering data.

    circle-info

    Please note that you can use ColdBox as a pure API solution with modern JavaScript frameworks for the front end like VueJS, Reactor, Angular, etc.

    Routing DSL

    The ColdBox Routing DSL will be used to register routes for your application, which exists in your application or module router object. Routing takes place using several methods inside the router, which are divided into the following 3 categories:

    1. Initiators - Starts a URL pattern registration, but does not fully register the route until a terminator is called (target).

    2. Modifiers - Modifies the pattern with extra metdata to listen to from the incoming request.

    Model Data Binding

    The framework also offers you the capability to bind incoming FORM/URL/REMOTE/XML/JSON/Structure data into your model objects by convention. This is done via capabilities. The easiest approach is to use our populateModel() function which will populate the object from many incoming sources:

    • request collection RC

    • Structure

    event={module:}{package.}{handler}{.action}
    // Call the users.cfc index() method
    index.cfm?event=users.index
    // Call the users.cfc index() method implicitly
    index.cfm?event=users
    // Call the users.cfc index() method via URL mappings
    index.cfm/users/index
    // Call the users.cfc index() method implicitly via URL mappings
    index.cfm/users
    //set a value for views to use
    event.setValue( "name", "Luis" );
    event.setPrivateValue( "name", "Luis" );
    
    // retrieve a value the user sent
    event.getValue( "name" );
    // retrieve a value the user sent or give me a default value
    event.getValue( "isChecked", false );
    
    // retrieve a private value
    event.getPrivateValue( "name" );
    // retrieve a private value or give me a default value
    event.getPrivateValue( "isChecked", false );
    
    // param a value
    event.paramValue( "user_id", "" );
    // param a private value
    event.paramPrivateValue( "user_id", "" );
    
    // remove a value
    event.removeValue( "name" );
    // remove a private value
    event.removePrivateValue( "name" );
    
    //check if value exists
    if( event.valueExists( "name" ) ){
    
    }
    //check if private value exists
    if( event.privateValueExists( "name" ) ){
    
    }
    
    // set a view for rendering
    event.setView( 'blog/index' );
    
    // set a layout for rendering
    event.setLayout( 'main' );
    
    // set a view and layout
    event.setView( view="blog/userinfo", layout="ajax" );
    // only fire for the actions: save(), delete()
    this.prehandler_only = "save,delete";
    // DO NOT fire for the actions: login(), doLogin(), logout()
    this.prehandler_except = "login,doLogin,logout"
    // Old Style
    http://localhost/index.cfm?event=home.about&page=2
    http://localhost/index.cfm?city=24&page=3&county=234324324
    // New Routing Style
    http://localhost/home/about/page/2
    http://localhost/dade/miami/page/3
    event.setView( 'view' );
    event.setView( 'mydirectory/myView' );

    Terminators - Finalizes the registration process usually by telling the router what happens when the route pattern is detected. This is refered to as the target.

    circle-exclamation

    Please note that order of declaration of the routes is imperative. Order matters.

    circle-info

    Please remember to check out the latest API Docsarrow-up-right for the latest methods and argument signatures.

    hashtag
    Initiators

    The following methods are used to initiate a route registration process.

    triangle-exclamation

    Please note that a route will not register unless a terminator is called or the inline target terminator is passed.

    • route( pattern, [target], [name=arguments.pattern] ) - Register a new route with optional target terminators and a name

    • get( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a GET http verb restriction

    • post( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a POST http verb restriction

    • put( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a PUT http verb restriction

    • patch( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a PATCH http verb restriction

    • options( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a OPTIONS http verb restriction

    • group( struct options, body ) - Group routes together with options that will be applied to all routes declared in the body closure/lambda.

    hashtag
    Modifiers

    Modifiers will tell the routing service about certain restrictions, conditions or locations for the routing process. It will not register the route just yet.

    • header( name, value, overwrite=true ) - attach a response header if the route matches

    • headers( map, overwrite=true ) - attach multiple response headers if the route matches

    • as( name ) - Register the route as a named route

    • rc( name, value, overwrite=true ) - Add an RC value if the route matched

    • rcAppend map, overwrite=true ) - Add multiple values to the RC collection if the route matched

    • prc( name, value, overwrite=true ) - Add an PRC value if the route matched

    • prcAppend map, overwrite=true ) - Add multiple values to the PRC collection if the route matched

    • withHandler( handler ) - Map the route to execute a handler

    • withAction( action ) - Map the route to execute a single action or a struct that represents verbs and actions

    • withModule( module ) - Map the route to a module

    • withNamespace( namespace ) - Map the route to a namespace

    • withSSL() - Force SSL

    • withCondition( condition ) - Apply a runtime closure/lambda enclosure

    • withDomain( domain ) - Map the route to a domain or subdomain

    • withVerbs( verbs ) - Restrict the route to listen to only these HTTP Verbs

    • packageResolver( toggle ) - Turn on/off convention for packages

    • valuePairTranslator( toggle ) - Turn on/off automatic name value pair translations

    hashtag
    Terminators

    Terminators finalize the routing process by registering the route in the Router.

    • end() - Register the route as it exists

    • toAction( action ) - Send the route to a specific action or RESTFul action struct

    • toView( view, layout, noLayout=false, viewModule, layoutModule ) - Send the route to a view/layout

    • toRedirect( target, statusCode=301 ) - Relocate the route to another event

    • to( event ) - Execute the event if the route matches

    • toHandler( handler ) - Execute the handler if the route matches

    • toResponse( body, statusCode=200, statusText="ok" ) - Inline response action

    • toModuleRouting( module ) - Send to the module router for evaluation

    • toNamespaceRouting( namespace ) - Send to the namespace router for evaluation

    [COLDBOX-974arrow-up-right] - Have the html helper manifests in local memory instead of the template cache to avoid cleanup issues

  • [COLDBOX-975arrow-up-right] - Remove unecessary locks for view path setups in the renderer

  • [COLDBOX-976arrow-up-right] - Remove unecessary lock in the bootstrap to get the controller reference, it's already there for the reload checks

  • [COLDBOX-979arrow-up-right] - Module service now profiles registration and activation into the logs with the version and path of a module

  • COLDBOX-890arrow-up-right
    COLDBOX-914arrow-up-right
    COLDBOX-935arrow-up-right
    COLDBOX-964arrow-up-right
    COLDBOX-967arrow-up-right
    COLDBOX-968arrow-up-right
    COLDBOX-978arrow-up-right
    COLDBOX-973arrow-up-right
    COLDBOX-965arrow-up-right
    COLDBOX-971arrow-up-right
    COLDBOX-972arrow-up-right
    CACHEBOX-67arrow-up-right
    WIREBOX-111arrow-up-right
    A Redirection to occur
    Greater application portability
  • URL's are more descriptive and easier to remember

  • ColdBox Route Visualizerarrow-up-right
    http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/web/context/RequestContext.html#renderData(arrow-up-right
    full docs

    getEnv

    ( key, defaultValue )

    Returns the server environment variable for key. Returns the defaultValue if it does not exist.

    ColdBox settings
    via dependency injection.
    hashtag
    ColdFusion 11 Support Dropped

    ColdFusion 11 support has been dropped. Adobe doesn't support them anymore, so neither do we.

    hashtag
    Setting Changes

    The following settings have been changed and altering behavior:

    • coldbox.autoMapModels is now defaulted to true

      • Which means that all your models will be MAPPED for you. If you have specific mappers in your config/WireBox.cfc make sure they override the mapping or turn this setting false.

    • coldbox.onInvalidEvent has been REMOVED in preference to coldbox.invalidEventHandler

    • coldbox.jsonPayloadToRC is now defaulted to true

    hashtag
    BuildLink( linkto ) argument removed

    The buildLink() method had an argument linkTo . It has now changed to to to provide simplification

    hashtag
    SES Interceptor Removed

    The SES interceptor has finally been removed. You can now remove it from your interceptor declarations. If you are relying on the SES interceptor for routing, then you will need to access the RoutingService via the following injection methods or retrieval methods:

    hashtag
    Method Changes

    hashtag
    getModel() Removed

    This method was marked for deprecation in ColdBox 5 and now it is removed. You can use the getInstance() method instead.

    hashtag
    WireBox Provider get() method to $get()

    All WireBox providers now implement the new interface which has changed the method of get() to $get() to avoid proxying to methods that already implement a get() method. So if you are using the get() method just update it to the new $get() method.

    hashtag
    getSetting()

    The getSetting() method does NOT include a fwSetting boolean argument anymore. You can now use the getColdBoxSetting() method instead.

    hashtag
    announceInterception( state, interceptData ) => announce( state, data )

    This method has now been deprecated in favor of its shorthand announce(). This method will still work but it will be removed in the next major version. So just rename it now. Also note that the interceptData has now changed to just data

    hashtag
    processState( state, interceptData ) => announce( state, data )

    This method was used in the event manager and interceptor service and has been marked for deprecation. Please use the method announce() instead. Which is also a consistency in naming now.

    hashtag
    setNextEvent() Removed

    The method setNextEvent() has been removed in favor of relocate(). We had deprecated this method in ColdBox 5.

    hashtag
    Interceptor Buffer Methods Removed

    These methods have been deprecated since version 4 and they are now removed.

    • getBufferObject()

    • getBufferString()

    • appendToBuffer()

    • clearBuffer()

    Every interception listener receives the buffer as an argument so there is no need to go to global functions for working with the buffer.

    hashtag
    Interceptor Arguments: interceptData => data

    All interceptors receive arguments when listening, we have renamed the interceptData to just data. The old approach still works but it is marked as deprecated. So just rename it to data

    hashtag
    System Path Changes

    • Default Bug Report Files are now located in /coldbox/system/exceptions/. Previously /coldbox/system/includes/

    So make sure you update your CustomErrorTemplate path to this new path:

    hashtag
    Rendering Changes

    The entire rendering mechanisms in ColdBox 6 have changed. We have retained backwards compatibility but there might be some loopholes that worked before that won't work now. Basically, the renderer is a singleton and each view renders in isolation. Meaning if a view sets a variable in it's variables scope NO OTHER view will have access to it.

    What's New
    A new handler called hello.cfc inside of the handlers folder
  • A view called index.cfm in the views/hello folder

  • An integration test at tests/specs/integration/helloTest.cfc.

  • hashtag
    Default URL Routing

    Now go to your browser and enter the following URL to execute the generated event:

    You will now see a big hello.index outputted to the screen. You have now created your first handler and view combination. However, how did this work? It works as ColdBox by convention creates another agreement with you on how to execute events, default URL routing.

    Your application router is located at : config/Router.cfc. It will include a few default routes for you and the following default URL route:

    This route tells ColdBox to look for the names of handlers (including directory names) and for names of the handler actions (functions). The ? on the :action portion denotes that the action might or might not exist in the URL. If it doesn't exist, then another convention is in play, the default action which is index.

    hashtag
    Handler Code

    Let's check out the handler code:

    As you can see, a handler is a simple CFC with functions on them. Each function maps to an action that is executed via the URL. The default action in ColdBox is index()which receives three arguments:

    • event - An object that represents the request and can modify the response. We call this object the request context.

    • rc - A struct that contains both URL/FORM variables (unsafe data)

    • prc - A secondary struct that is private only settable from within your application (safe data)

    circle-info

    The event object is used for many things, in the case of this function we are calling a setView() method which tells the framework what view to render to the user once execution of the action terminates.

    circle-check

    Tip: The view is not rendered in line 7, but rendered after the execution of the action by the framework.

    hashtag
    Executing Events

    Did you detect a convention here?

    The sections in the URL are the same as the name of the event handler CFC (hello.cfc) and method that was generated index(). By convention, this is how you execute events in ColdBox by leveraging the following URL pattern that matches the name of a handler and action function.

    circle-check

    Tip : You can also nest handlers into folders and you can also pass the name of the folder(s) as well.

    If no action is defined in the URL then the default action of index will be used.

    All of this URL magic happens thanks to the URL mappings capabilities in ColdBox. By convention, you can write beautiful URLs that are RESTFul and by convention. You can also extend them and create more expressive URL Mappings by leveraging the config/Router.cfc which is your application router.

    circle-check

    Tip: Please see the event handlers guide for more in-depth information.

    hashtag
    My First Virtual Event

    Now let's create a virtual event, which is basically just a view we want to execute with no event handler controller needed. This is a great way to incorporate non-mvc files into ColdBox. Migrating from a traditional application?

    Open the view now (/views/virtual/hello.cfm) and add the following:

    Then go execute the virtual event:

    You will get the Hello From ColdBox Land! displayed! This is a great way to create tests or even bring in legacy/procedural templates into an MVC framework.

    circle-check

    Tip: You can see our layouts and views section for more in-depth information.

    Using the route() method and the name argument
  • Using the as() method

  • circle-info

    If you do not pass the name argument to the route() method, we will use the pattern as the name of the route.

    hashtag
    Generating URLs to Named Routes

    You will generate URLs to named routes by leveraging the route() method in the request context object (event).

    Let's say you register the following named routes:

    Then we can create routing URLs to them easily with the event.route() method:

    hashtag
    Inspecting The Current Route

    The request context object (event) also has some handy methods to tell you the name or even the current route that was selected for execution:

    • getCurrentRouteName() - Gives you the name of the current route, if any

    • getCurrentRoute() - Gives you the currently executed route

    • getCurrentRoutedURL() - Gives you the complete routed URL pattern that matched the route

    • getCurrentRoutedNamespace() - Gives you the current routed namespace, if any

    ) variables:

    hashtag
    Optional Placeholders

    Sometimes we will want to declare routes that are very similar in nature and since order matters, they need to be declared in the right order. Like this one:

    However, we just wrote 4 routes for this approach when we can just use optional variables by using the ? symbol at the end of the placeholder. This tells the processor to create the routes for you in the most detailed manner first instead of you doing it manually.

    triangle-exclamation

    Caution Just remember that an optional placeholder cannot be followed by a non-optional one. It doesn't make sense.

    hashtag
    Numeric Placeholders

    ColdBox gives you also the ability to declare numerical only routes by appending -numeric to the variable placeholder so the route will only match if the placeholder is numeric. Let's modify the route from above.

    This route will only accept years, months and days as numbers.

    hashtag
    Alpha Placeholders

    ColdBox gives you also the ability to declare alpha only routes by appending -alpha to the variable placeholder so the route will only match if the placeholder is alpha only.

    This route will only accept page names that are alpha only.

    There are two ways to place a regex constraint on a placeholder, using the -regex: placeholder or adding a constraints structure to the route declaration.

    hashtag
    Regular Expression Placeholders

    You can also have the ability to declare a placeholder that must match a regular expression by using the -regex( {regex_here} ) placeholder.

    The rc variable format must match the regex supplied: (xml|json)

    hashtag
    Regular Expression Constraints

    You can also apply a structure of regular expressions to a route instead of inlining the regular expressions in the placeholder location. You will do this using the constraints() method of the router.

    The key in the structure must match the name of the placeholder and the value is a regex expression that must be enclosed by parenthesis ().

    json

  • xml

  • query

  • This will try to match incoming variable names to setters or properties in your domain objects and then populate them for you. It can even do ORM entities with ALL of their respective relationships. Here is a snapshot of the method:

    Let's do a quick example:

    Person.cfc

    editor.cfm

    Event Handler -> person.cfc

    In the dump you will see that the name and email properties have been bound.

    WireBox's object populationarrow-up-right
    component accessors="true"{
    
        property name="name";
        property name="email";
    
        function init(){
            setName('');
            setEmail('');
        }
    }
    config/Router.cfc
    function configure(){
    
        // Routing with placeholders to an event with placeholders
        route( "/blog/:year-numeric{4}/:month?/:day?" )
            .to( "blog.list" );
    
        // Redirects
        route( "/old/book" )
            .toRedirect( "/mybook" );
    
        // Responses
        route( "/echo" ).toResponse( (event,rc,prc) => {
            return "hello luis";
        } );
    
        // Shortcut to above
        route( "/echo", (event,rc,prc) => {
            return "hello luis";
        } );
    
        // Show view
        route( "/contact-us" )
            .toView( "main/contact" );
    
        // Direct to handler with action from URL
        route( "/users/:action" )
            .toHandler( "users" );
    
        // Inline pattern + target and name
        route( pattern="/wiki/:page", target="wiki.show", name="wikipage" );
    
    }
    box install route-visualizer
    /**
    * Executes events with full life-cycle methods and returns the event results if any were returned.
    * @event The event string to execute, if nothing is passed we will execute the application's default event.
    * @prePostExempt If true, pre/post handlers will not be fired. Defaults to false
    * @private Execute a private event if set, else defaults to public events
    * @defaultEvent The flag that let's this service now if it is the default event running or not. USED BY THE FRAMEWORK ONLY
    * @eventArguments A collection of arguments to passthrough to the calling event handler method
    * @cache Cached the output of the runnable execution, defaults to false. A unique key will be created according to event string + arguments.
    * @cacheTimeout The time in minutes to cache the results
    * @cacheLastAccessTimeout The time in minutes the results will be removed from cache if idle or requested
    * @cacheSuffix The suffix to add into the cache entry for this event rendering
    * @cacheProvider The provider to cache this event rendering in, defaults to 'template'
    */
    function runEvent(
    	event="",
    	boolean prePostExempt=false,
    	boolean private=false,
    	boolean defaultEvent=false,
    	struct eventArguments={},
    	boolean cache=false,
    	cacheTimeout="",
    	cacheLastAccessTimeout="",
    	cacheSuffix="",
    	cacheProvider="template"
    )
    //public event
    runEvent( 'users.save' );
    
    //post exempt
    runEvent( event='users.save', prePostExempt=true );
    
    //Private event
    runEvent( event='users.persist', private=true );
    
    // Run event as a widget
    <cfoutput>#runEvent(
        event         = 'widgets.userInfo',
        prePostExempt = true,
        eventArguments= { widget=true }
    )#</cfoutput>
    
    // Run with Caching
    runEvent( event="users.list", cache=true, cacheTimeout=30 );
    // handler responding to widget call
    function userInfo( event, rc, prc, widget=false ){
    
        prc.userInfo = userService.get( rc.id );
    
        // set or widget render
        if( arguments.widget ){
            return renderView( "widgets/userInfo" );
        }
    
        // else set view
        event.setView( "widgets/userInfo" );
    }
    runEvent( event="users.widget", eventArguments={ max=10, page=1 }, cache=true );
    
    // Cached as a new key
    runEvent( event="users.widget", eventArguments={ max=10, page=2 }, cache=true );
    
    function index( event, rc, prc ){
        // Inline
        event.setView( view="main/index", layout="2columns" );
        
        // Concatenated
        event.setView( "main/index" )
            .setLayout( "2columns" );
    }
    function index( event, rc, prc ){
        // Inline
        event.setView( view="widgets/users", nolayout=true );   
    }
    * @view The name of the view to set. If a layout has been defined it will assign it, else if will assign the default layout. No extension please
    * @args An optional set of arguments that will be available when the view is rendered
    * @layout You can override the rendering layout of this setView() call if you want to. Else it defaults to implicit resolution or another override.
    * @module The explicit module view
    * @noLayout Boolean flag, wether the view sent in will be using a layout or not. Default is false. Uses a pre set layout or the default layout.
    * @cache True if you want to cache the rendered view.
    * @cacheTimeout The cache timeout in minutes
    * @cacheLastAccessTimeout The last access timeout in minutes
    * @cacheSuffix Add a cache suffix to the view cache entry. Great for multi-domain caching or i18n caching.
    * @cacheProvider The cache provider you want to use for storing the rendered view. By default we use the 'template' cache provider
    * @name This triggers a rendering region.  This will be the unique name in the request for specifying a rendering region, you can then render it by passing the unique name to renderView();
    // Cache in the template cache for the default amount of time
    event.setView( view='myView', cache=true );
    // Cache in the template cache for up to 60 minutes, or 20 minutes after the last time it's been used
    event.setView( view='myView', cache=true, cacheTimeout=60, cacheLastAccessTimeout=20 );
    // Cache a different version of the view for each language the site has
    event.setView( view='myView', cache=true, cacheSuffix=prc.language );
    var viewData = {
      data1 = service.getData1(),
      data2 = service.getData2()
    };
    
    event.setView( view='myView', args=viewData );
    <cfoutput>
      Data 1: #args.data1#<br>
      Data 2: #args.data2#
    </cfoutput>
    event.noRender();
    contacts.cfc
    any function index( event, rc, prc ){
        return contactService.getAll();    
    }
    contacts.cfc
    any function index( event, rc, prc ){
        prc.aContacts = contactService.getAll();    
        event.renderData( data=prc.aContacts, formats="xml,json,pdf,html" );
    }
    event.renderData( data=prc.aContacts, formats="xml,json,pdf,html" );
    # Default: The view is presented using no extension or html,cfm
    http://localhost:{port}/contacts/index
    http://localhost:{port}/contacts/index.html
    http://localhost:{port}/contacts/index.cfm
    
    # JSON output
    http://localhost:{port}/contacts/index.json
    # OR Accepts: application/json
    
    # XML output 
    http://localhost:{port}/contacts/index.xml
    # OR Accepts: application/xml
    
    # PDF output
    http://localhost:{port}/contacts/index.pdf
    # OR Accepts: application/pdf
    
    // Restuful Route
    route( 
        pattern="/api/contacts",
        target="contacts.index",
        name="api.contacts"
    );
    
    // Default Route
    route( ":handler/:action?" ).end();
    http://localhost:{port}/api/contacts.json
    environments = {
        // The key is the name of the environment
        // The value is a list of regex to match against cgi.http_host
        development = "^cf2016.,^lucee.,localhost",
        staging = "^stg"
    };
    if ( getSetting('environment') == 'development' ){
        doSomeMajik();
    }
    /**
    * Executed whenever the development environment is detected
    */
    function development(){
        // Override coldbox directives
        coldbox.handlerCaching = false;
        coldbox.eventCaching = false;
        coldbox.debugPassword = "";
        coldbox.reinitPassword = "";
    
        // Add dev only interceptors
        arrayAppend( interceptors, {class="#appMapping#.interceptors.CustomLogger} );
    }
    string public detectEnvironment(){
    }
    Application.cfc
    
    component {
    
        variables.util = new coldbox.system.core.util.Util();
    
        this.datasources[ "my_datasource" ] = {
            driver = util.getSystemSetting( "DB_DRIVER" ),
            host = util.getSystemSetting( "DB_HOST" ),
            port = util.getSystemSetting( "DB_PORT" ),
            database = util.getSystemSetting( "DB_DATABASE" ),
            username = util.getSystemSetting( "DB_USERNAME" ),
            password = util.getSystemSetting( "DB_PASSWORD" )
        };
    
    }
    controller.getRoutingService()
    
    property name="routingService" inject="coldbox:routingService"
    property name="userService" inject="provider:UserService";
    
    function getData(){
      return userService.$get().search();
    }
    getSetting( "version", true ) ==> getColdBoxSetting( "version" )
    announce( state, data )
    function state( event, data, rc, prc, buffer ){
    
    }
    function preProcess( event, data, buffer, rc, prc )
    
    ## instead of 
    
    function preProcess( event, interceptData, buffer, rc, prc )
    coldbox.customErrorTemplate = "/coldbox/system/includes/BugReport.cfm";
    // to
    coldbox.customErrorTemplate = "/coldbox/system/exceptions/BugReport.cfm";
    // Or use our new template
    coldbox.customErrorTemplate = "/coldbox/system/exceptions/Whoops.cfm";
    coldbox create handler name="hello" actions="index"
    # With rewrites enabled
    http://localhost:{port}/hello/index
    // Conventions based routing
    route( ":handler/:action?" ).end();
    component{
    
        /**
         * Default Action
         */
         function index( event, rc, prc ){
            event.setView( "hello/index" );
         }
    
    
    }
    http://localhost:{port}/folder/handler/action
    http://localhost:{port}/handler/action
    http://localhost:{port}/handler
    coldbox create view name="virtual/hello"
    <h1>Hello from ColdBox Land!</h1>
    http://localhost:{port}/virtual/hello
    // Using the pattern by convention
    route( pattern="/healthcheck" ).to( "healthcheck" );
    
    // Using the name argument
    route( 
        pattern = "/users/list", 
        target = "users.index", 
        name = "usermanager" 
    );
    
    route( 
        pattern = "/user/:id/profile", 
        target = "users.show", 
        name = "userprofile"
    );
    
    // Using the as() method
    route( "/users/:id/profile" )
      .as( "usersprofile" )
      .to( "users.show" )
    route(
        // The name of the route
        required name,
        // The params to pass, can be a struct or array
        struct params={},
        // Force or un-force SSL, by default we keep the same protocol of the request
        boolean ssl
    );
    route( 
        pattern = "/users/list", 
        target = "users.index", 
        name = "usermanager" 
    );
    
    route( 
        pattern = "/user/:id/profile", 
        target = "users.show", 
        name = "userprofile"
    );
    <!-- Named Route with no params -->
    <a href="#event.route( 'usermanager' )#">Manage Users</a>
    
    <!-- Named Route with struct params -->
    <a href="#event.route( 'userprofile', { id = 3 } )#">View User</a>
    
    <!-- Named Route with array params -->
    <a href="#event.route( 'userprofile', [ 3 ] )#">View User</a>
    
    <a href="#event.route( '/healthcheck' )#">Health check</a>
    route( "blog/:year/:month?/:day?", "blog.index" );
    http://localhost/blog/2012/12/22 -> rc.year=2012, rc.month=12, rc.day=22
    http://localhost/blog/2012/12-> rc.year=2012, rc.month=12
    http://localhost/blog/2012-> rc.year=2012
    route( "/blog/:year-numeric/:month-numeric/:day-numeric" );
    route( "/blog/:year-numeric/:month-numeric" );
    route( "/blog/:year-numeric/" );
    route( "/blog/" );
    route( "/blog/:year-numeric?/:month-numeric?/:day-numeric?" );
    route( "blog/:year-numeric/:month-numeric?/:day-numeric?", "blog.index" );
    route( "wiki/:page-alpha", "wiki.show" );
    // route with regex placeholders
    route(
        pattern="/api/:format-regex:(xml|json)/",
        target="api.execute"
    );
    // route with custom constraints
    route(
        pattern = "/api/:format/:entryID",
        target  = "api.execute"
    ).constraints( {
        format  = "(xml|json)",
        entryID = "([0-9]{4})" 
    } );
    /**
    * Populate a model object from the request Collection or a passed in memento structure
    * @model The name of the model to get and populate or the acutal model object. If you already have an instance of a model, then use the populateBean() method
    * @scope Use scope injection instead of setters population. Ex: scope=variables.instance.
    * @trustedSetter If set to true, the setter method will be called even if it does not exist in the object
    * @include A list of keys to include in the population
    * @exclude A list of keys to exclude in the population
    * @ignoreEmpty Ignore empty values on populations, great for ORM population
    * @nullEmptyInclude A list of keys to NULL when empty
    * @nullEmptyExclude A list of keys to NOT NULL when empty
    * @composeRelationships Automatically attempt to compose relationships from memento
    * @memento A structure to populate the model, if not passed it defaults to the request collection
    * @jsonstring If you pass a json string, we will populate your model with it
    * @xml If you pass an xml string, we will populate your model with it
    * @qry If you pass a query, we will populate your model with it
    * @rowNumber The row of the qry parameter to populate your model with
    */
    function populateModel(
    	required model,
    	scope="",
    	boolean trustedSetter=false,
    	include="",
    	exclude="",
    	boolean ignoreEmpty=false,
    	nullEmptyInclude="",
    	nullEmptyExclude="",
    	boolean composeRelationships=false,
    	struct memento=getRequestCollection(),
    	string jsonstring,
    	string xml,
    	query qry
    ){
    <cfoutput>
    <h1>Funky Person Form</h1>
    #html.startForm(action='person.save')#
    
        #html.textfield(label="Your Name:",name="name",wrapper="div")#
        #html.textfield(label="Your Email:",name="email",wrapper="div")#
    
        #html.submitButton(value="Save")#
    
    #html.endForm()#
    </cfoutput>
    component{
    
        function editor(event,rc,prc){
            event.setView("person/editor");        
        }
    
        function save(event,rc,prc){
    
            var person = populateModel( "Person" );
    
            writeDump( person );abort;
        }
    
    }

    ConfigSettings : Your application settings

    You can use the following methods to retrieve/set/validate settings in your handlers/layouts/views and interceptors:

    You can also get access to these methods in handlers via the ColdBox Controller component:

    or using the application scope from modules and other locations where controller isn't injected:

    hashtag
    Injecting Settings

    You can use the WireBox injection DSL to inject settings in your models or non-ColdBox objects. Below are the available DSL notations:

    • coldbox:setting:{key} : Inject a specified config setting key

    • coldbox:coldboxSetting:{key} : Inject a specified ColdBox setting key

    • coldbox:configSettings : Inject a reference to the application settings structure

    • coldbox:coldboxSettings : Inject a reference to the ColdBox System settings structure

    The ColdBox app mapping

    coldboxVersion

    The version of the framework

    controller

    The ColdBox running app controller

    logBoxConfig

    A reference to a LogBox configuration object

    getJavaSystem()

    Function to get access to the java system

    getSystemSetting()

    Retrieve a Java System property or env value by name. It looks at properties first then environment variables

    getSystemProperty()

    Retrieve a Java System property value by key

    getEnv()

    Retrieve a Java System environment value by name

    webMapping

    The application's web mapping

    hashtag
    Configuration Storage

    Once the application starts up, a reference to the instantiated configuration CFC will be stored in the configuration settings inside the ColdBox Main Controller (application.cbController) with the key coldboxConfig. You can then retrieve it later in your handlers, interceptors, modules, etc if you need to.

    hashtag
    Configuration Interceptor

    ColdBox Event Listeners

    Another cool concept for the Configuration CFC is that it is also registered as a ColdBox Interceptor once the application starts up automatically for you. You can create functions that will listen to application events by simply registering them by name:

    triangle-exclamation

    Note that the config CFC does not have the same variables mixed into it that a "normal" interceptor has. You can still access everything you need, but will need to get it from the controller in the variables scope.

    Property

    Description

    ColdBox Interceptor
    ColdBox.cfc EcoSystem

    appMapping

    hashtag
    Conventions

    Let's do a recap of our conventions for layouts and view locations:

    hashtag
    ColdBox Renderer

    It is imperative to know who does the rendering in ColdBox and that is the Renderer class that you can see from our diagram above. As you can tell from the diagram, it includes your layouts and/or views into itself in order to render out content. So by this association and inheritance all layouts and views have some variables and methods at their disposal since they get absorbed into the object. You can visit the API docsarrow-up-right to learn about all the Renderer methods.

    All of the following property members exist in all layouts and views rendered by the Renderer:

    Property

    Description

    event

    A reference to the Request Context object

    rc

    A reference to the request collection inside of the request context (For convenience)

    prc

    A reference to the private request collection inside of the request context (For convenience)

    html

    A reference to the that can help you build interactive and safe HTML

    cacheBox

    A reference to the framework factory (coldbox.system.cache.CacheFactory)

    As you can see, all views and layouts have direct reference to the request collections so it makes it incredibly easy to get and put data into it. Also, remember that the Renderer inherits from the Framework SuperType so all methods are at your disposal if needed.

    hashtag
    Injecting In Your Models

    You can also inject the ColdBox Renderer into your models so you can render email templates, views, etc. directly from your model code:

    circle-info

    In previous versions you would need to use a provider: syntax due to the Renderer being a transient. This is no longer true in ColdBox 6.0.

    layouts
    event handlers
    ColdBox Modules

    What's New With 6.5.x

    July 9th, 2021

    hashtag
    Compatibility Notes

    Please note that the following ticket corrects behavior in ColdBox that MIGHT affect interceptors that have injected dependencies that have the same name as application helper methods from other modules.

    Example:

    The interceptor above has a dependency of auth from the cbauth module. However, the cbauth module also has an application helper called auth. So at runtime, this will throw an exception:

    This is because now we can't inject the cbauth mixin because we already have a cbauth dependency injected. The resolution, is to RENAME the injection variables so they don't collide with module application helpers.

    hashtag
    6.5.2 Release Notes - July 14, 2021

    Regression

    • Module helpers no longer injected/mixed into interceptors

    hashtag
    6.5.1 Release Notes - July 12th, 2021

    hashtag
    Bug

    • Module helpers no longer injected/mixed into interceptors

    hashtag
    Improvement

    • Update BeanPopulator for Hibernate 5 detection in Lucee new extension

    • Added back the finally block, just to make sure cleanup are done in bootstrap reinits

    hashtag
    6.5.0 Release Notes - July 9th, 2021

    hashtag
    Bugs

    fix lastBusinessDay tests to reflect if the now is the actual last business day of the month

    ColdBox does not allow for non-existent client cookies when using Flash RAM

    ACF 2021 introduced getTimezone()

    What's New With 6.4.0

    ColdBox 6.4.0 is more of a major than a minor release due to the amount of work we have done to bring you one of the most revolutionary features of this framework: Scheduled Tasks.

    hashtag
    ColdBox Scheduled Tasks

    Scheduled tasks have always been a point of soreness for many developers in ANY language. Especially choosing where to place them for execution: should it be cron? windows task scheduler? ColdFusion engine? Jenkins, Gitlab? and the list goes on and on.

    ColdBox Scheduled Tasks

    The ColdBox Scheduled Tasks offers a fresh, programmatic and human approach to scheduling tasks on your server and multi-server application. It allows you to define your tasks in a portable Scheduler we lovingly call the Scheduler.cfc which not only can be used to define your tasks, but also monitor all of their life-cycles and metrics of tasks. Since ColdBox is also hierarchical, it allows for every single ColdBox Module to also define a Scheduler and register their own tasks as well. This is a revolutionary approach to scheduling tasks in an HMVC application.

    You can learn all about them in our two sections:

    hashtag
    Release Notes

    Bugs

    • Fixes issues with Adobe losing App Context in Scheduled Tasks. You can now run scheduled tasks in Adobe with full app support.

    • When running scheduled tasks in ACF loading of contexts produce a null pointer exception

    Installing ColdBox

    Welcome to the world of ColdBox!

    We are excited you are taking this development journey with us. Before we get started with ColdBox let's install CommandBox CLI, which will allow you to install/uninstall dependencies, start servers, have a REPL tool and much more.

    hashtag
    IDE Tools

    ColdBox has the following supported IDE Tools:

    • Sublime -

    • VSCode -

    • CFBuilder -

    hashtag
    CommandBox CLI

    The first step in our journey is to CommandBox. is a ColdFusion (CFML) Command Line Interface (CLI), REPL, Package Manager and Embedded Server. We will be using CommandBox for almost every exercise in this book and it will also allow you to get up and running with ColdFusion and ColdBox in a much speedier manner.

    circle-check

    However, you can use your own ColdFusion server setup as you see fit. We use CommandBox as everything is scriptable, portable and fast!

    hashtag
    Download CommandBox

    You can download CommandBox from the official site: and install in your preferred Operating System (Windows, Mac, *unix). CommandBox comes in two flavors:

    1. No Java Runtime (30mb)

    2. Embedded Runtime (80mb)

    So make sure you choose your desired installation path and follow the instructions here:

    hashtag
    Starting CommandBox

    Once you download and expand CommandBox you will have the box.exe or box binary, which you can place in your Windows Path or *Unix /usr/bin folder to have it available system wide. Then just open the binary and CommandBox will unpack itself your user's directory: {User}/.CommandBox. This happens only once and the next thing you know, you are in the CommandBox interactive shell!

    We will be able to execute a-la-carte commands from our command line or go into the interactive shell for multiple commands. We recommend the interactive shell as it is faster and can remain open in your project root.

    circle-info

    All examples in this book are based on the fact of having an interactive shell open.

    hashtag
    Installing ColdBox

    To get started open the CommandBox binary or enter the shell by typing box in your terminal or console. Then let's create a new folder and install ColdBox into a directory.

    CommandBox will resolve coldbox from ForgeBox (), use the latest version available, download and install it in this folder alongside a box.json file which represents your application package.

    circle-info

    Tip : You can also install the latest bleeding edge version by using the coldbox@be slug instead, or any previous version.

    CommandBox can now track this version of ColdBox for you in this directory. In the we will scaffold a ColdBox application using an application template.

    circle-check

    You can find many scaffolding templates for ColdBox in our Github organization:

    hashtag
    Uninstalling ColdBox

    To uninstall ColdBox from this application folder just type uninstall coldbox. Try it out!

    hashtag
    Updating ColdBox

    To update ColdBox from a previous version, just type update coldbox.

    Installation

    Get up and running with ColdBox easily.

    Welcome to the world of ColdBox!

    We are excited you are taking this development journey with us. Before we get started with ColdBox let's install CommandBox CLI, which will allow you to install/uninstall dependencies, start servers, have a REPL tool and much more.

    hashtag
    IDE Tools

    ColdBox has the following supported IDE Tools:

    • Sublime -

    • VSCode -

    • CFBuilder -

    hashtag
    CommandBox CLI

    The first step in our journey is to CommandBox. is a ColdFusion (CFML) Command Line Interface (CLI), REPL, Package Manager and Embedded Server. We will be using CommandBox for almost every excercise in this book and it will also allow you to get up and running with ColdFusion and ColdBox in a much speedier manner.

    Note : However, you can use your own ColdFusion server setup as you see fit. We use CommandBox as everything is scriptable and fast!

    hashtag
    Download CommandBox

    You can download CommandBox from the official site: and install in your preferred Operating System (Windows, Mac, *unix). CommandBox comes in two flavors:

    1. No Java Runtime (80mb)

    2. Embedded Runtime (120mb)

    So make sure you choose your desired installation path and follow the instructions here:

    hashtag
    Starting CommandBox

    Once you download and expand CommandBox you will have the box.exe or box binary, which you can place in your Windows Path or *Unix /usr/bin folder to have it available system wide. Then just open the binary and CommandBox will unpack itself your user's directory: {User}/.CommandBox. This happens only once and the next thing you know, you are in the CommandBox interactive shell!

    We will be able to execute a-la-carte commands from our command line or go into the interactive shell for multiple commands. We recommend the interactive shell as it is faster and can remain open in your project root.

    circle-info

    All examples in this book are based on the fact of having an interactive shell open.

    hashtag
    Installing ColdBox

    To get started open the CommandBox binary or enter the shell by typing box in your terminal or console. Then let's create a new folder and install ColdBox into a directory.

    CommandBox will resolve coldbox from FORGEBOX (), use the latest version available, download and install it in this folder alongside a box.json file which represents your application package.

    circle-info

    You can also install the latest bleeding edge version by using the coldbox@be slug instead, or any previous version.

    That's it! CommandBox can now track this version of ColdBox for you in this directory.

    hashtag
    Scaffolding ColdBox Applications

    CommandBox comes with a coldbox create app command that can enable you to create application skeletons using one of our official skeletons or by creating application template:

    • AdvancedScript (default): A script based advanced template

    • elixir : A ColdBox Elixir based template

    • ElixirBower : A ColdBox Elixir + Bower based template

    circle-check

    You can find many scaffolding templates for ColdBox in our Github organization:

    Type coldbox create app help in CommandBox to get tons of help for scaffolding apps.

    hashtag
    Uninstalling ColdBox

    To uninstall ColdBox from this application folder just type uninstall coldbox.

    hashtag
    Updating ColdBox

    To update ColdBox from a previous version, just type update coldbox.

    Around Advices

    Around advices are the most powerful of all as you completely hijack the requested action with your own action that looks, smells and feels exactly as the requested action. This is usually referred to as a proxy design pattern.arrow-up-right

    This will allow you to run both before and after advices but also surround the method call with whatever logic you want like transactions, try/catch blocks, locks or even decide to NOT execute the action at all.

    You can do it globally by using the aroundHandler() method or targeted to a specific action around{actionName}().

    Examples

    The arguments received by these interceptors are:

    • event : The request context reference

    • targetAction : The function pointer to the action that got the around interception. It will be your job to execute it (Look at samples)

    • eventArguments

    hashtag
    Exceptions & Only Lists

    You can fine tune these interception methods by leveraging two public properties in the handler:

    • this.aroundhandler_only : A list of actions that the aroundHandler() action will fire ONLY!

    • this.aroundhandler_except : A list of actions that the aroundHandler() action will NOT fire on

    Viewlets - Reusable Events

    A viewlet is a self sufficient view or a widget that can live on its own, its data is pre-fetched and can just be renderer anywhere in your system.

    What in the world is this? Well, imagine a portal, in which each section of the portal is self-sufficient, with controls and data. You don't want to call all the handlers for this data for every single piece of content. It's not efficient, you need to create a separation. Well, a viewlet is such a separation that provides you with the ability to create reusable events. So how do we achieve this?

    1. You will use the method runEvent() anywhere you want a viewlet to be displayed or the content rendered. This calls an internal event that will be in charge to prepare and render the viewlet.

    2. Create the portable event but make sure it returns the produced content.

    hashtag
    Simple Example

    This code just renders out the results of a runEvent() method call. Please note that you can pass in arguments to the event using the eventArguments argument. This makes the event act like a method call with arguments to it. Remember that all events you call via runEvent() will share the same RC/PRC.

    circle-info

    I would suggest you look at to discover all arguments to the runEvent() method call.

    hashtag
    Event Code

    As you can see from the code above, the handler signature can accept arguments which are passed via the eventArguments structure. It talks to a service layer and place some data on the private request collection the viewlet will use. It then returns the results of a renderView() call that will render out the exact viewlet I want. You can be more creative and do things like:

    • render a layout + view combo

    • render data

    • return your own custom strings

    circle-exclamation

    Caution We would suggest you namespace or prefix your private request collection variables for viewlets in order to avoid collisions from multiple viewlet events in the same execution thread or instead pass the necessary arguments into a view via the args argument.

    hashtag
    View Code

    The view is a normal standard view, it doesn't even know it is a viewlet, remember, views are DUMB!

    hashtag
    Content Variables

    A content variable is a variable that contains HTML/XML or any kind of visual content that can easily be rendered anywhere. So instead of running the viewlet event in the view, you can abstract it to the controller layer and assign the output to a content variable:

    So how do I render it?

    Another example, is what if we do not know if the content variable will actually exist? How can we do this? Well, we use the event object for this and its magic getValue() method.

    So now, if no content variable exists, an empty string will be rendered.

    triangle-exclamation

    Important String manipulation in Java relies on immutable structures, so performance penalties might ensue. If you will be doing a lot of string manipulation, concatenation or rendering, try to leverage native java objects: StringBuilder or StringBuffer

    Working With Event Handlers

    Event handlers are the controller layer in ColdBox and is what you will be executing via the URLor a FORMpost. All event handlers are singletons, which means they are cached for the duration of the application, so always remember to var scope your variables in your functions.

    circle-check

    Tip: For development we highly encourage you to turn handler caching

    Adding A Model

    Let's complete our saga into MVC by developing the M, which stands for . This layer is all your business logic, queries, external dependencies, etc. of your application, which represents the problem to solve or the domain to solve.

    hashtag
    WireBox

    This layer is controlled by , the dependency injection framework within ColdBox, which will give you the flexibility of wiring your objects and persisting them for you.

    Request Context

    On every request to a ColdBox event, the framework creates an object that models the incoming request. This object is called the Request Context Object. This object will be passed to an and will be processed by an and is by convention called an event)

    It contains the incoming FORM/REMOTE/URL variables the client sent in and the object lives in the ColdFusion request scope and you will use to for responses and interacting with client data.

    circle-info

    Application Router

    Every ColdBox application has a URL router and can be located by convention at config/Router.cfc. This is called the application router and it is based on the router core class: coldbox.system.web.routing.Router. Here is where you will configure router settings and define routes using our routing DSL.

    circle-info

    Please see the latest for investigating all the methods and properties of the Router.

    Views

    Views are HTML content that can be rendered inside of a layout or by themselves. They can be either rendered on demand or by being set by an event handler. Views can also produce any type of content apart from HTML like JSON/XML/WDDX via our view renderer that we will discover also. So get ready for some rendering goodness!

    hashtag
    Setting Views For Rendering

    Usually, event handlers are the objects in charge of setting views for rendering. However, ANY object that has access to the request context object can do this also. This is done by using the setView() method in the request context object.

    Configuration Directives

    The basic configuration object has 1 method for application configuration called configure() where you will place all your configuration directives and settings:

    hashtag
    Directives

    Inside of this configuration method you will place several core and third-party configuration structures that can alter your application settings and behavior. Below are the core directives you can define:

    FrameworkSuperType.cfc
    /**
     * Get a setting from the system
     *
     * @name The key of the setting
     * @defaultValue If not found in config, default return value
     *
     * @throws SettingNotFoundException
     *
     * @return The requested setting
     */
    function getSetting( required name, defaultValue )
    
    /**
     * Get a ColdBox setting
     *
     * @name The key to get
     * @defaultValue The default value if it doesn't exist
     *
     * @throws SettingNotFoundException
     *
     * @return The framework setting value
     */
    function getColdBoxSetting( required name, defaultValue )
    
    /**
     * Check if the setting exists in the application
     *
     * @name The key of the setting
     */
    boolean function settingExists( required name )
    
    /**
     * Set a new setting in the system
     *
     * @name The key of the setting
     * @value The value of the setting
     *
     * @return FrameworkSuperType
     */
    any function setSetting( required name, required value )
    
    /**
     * Get a module's settings structure or a specific setting if the setting key is passed
     *
     * @module The module to retrieve the configuration settings from
     * @setting The setting to retrieve if passed
     * @defaultValue The default value to return if setting does not exist
     *
     * @return struct or any
     */
    any function getModuleSettings( required module, setting, defaultValue )
    controller.getSetting()
    controller.getColdBoxSetting()
    controller.setSetting()
    controller.settingExists()
    controller.getConfigSettings()
    controller.getColdBoxSettings()
    application.cbController.getSetting()
    application.cbController.setSetting()
    application.cbController.settingExists()
    application.cbController.getConfigSettings()
    application.cbController.getColdBoxSettings()
    component{
    
        property name="mysetting"    inject="coldbox:setting:mysetting";
        property name="path"         inject="coldbox:coldboxSetting:path";
        property name="config"       inject="coldbox:configSettings";
        property name="settings"     inject="coldbox:coldboxSettings";
    
    }
    // retrieve it
    config = getSetting( 'coldboxConfig' );
    
    // inject it
    property name="config" inject="coldbox:setting:coldboxConfig";
    function preProcess( event, interceptData, buffer, rc, prc ){
        writeDump( 'I just hijacked your app!' );abort;
    }
    function preRender( event, interceptData, buffer, rc, prc ){
        controller.getWirebox().getInstance( 'loggerService' ).doSomething();
    }
    + application
      + layouts
      + views
    component{
    
        property name="renderer" inject="coldbox:renderer";
    
        function renderSomething(){
            return renderer.renderView( view="mail/mymail", args={} );
        }
    }
    component 
    {
    
        // inject cbauth so we can use it in our interceptor
        property name="auth" inject="provider:authenticationService@cbauth";
    
        function preProcess( event ) {
    
            writeDump( auth.isLoggedIn() );
    
        }
    
    }
    users.list => users.aroundHandler() <=> list()
    etc
    the API docsarrow-up-right
    circle-info

    Setting a view does not mean that it gets rendered immediately. It means that it is deposited in the request context. The framework will later on in the execution process pick those variables up and do the actual rendering. To do immediate rendering you will use the inline rendering methods describe later on.

    We use the setView() method to set the view views/general/index.cfm to be rendered. Now the cool thing about this, is that we can override the view to be rendered anytime during the flow of the request. So the last process to execute the setView() method is the one that counts. Also notice a few things:

    • No .cfm extension is needed.

    • You can traverse directories by using / like normal cfinclude notation.

    • The view can exist in the conventions directory views or in your configured external locations

    • You did not specify a layout for the view, so the application's default layout (main.cfm) will be used.

    triangle-exclamation

    It is best practice that view locations should simulate the event. So if the event is general.index, there should be a general folder in the root views folder with a view called index.cfm.

    Let's look at the view code:

    I am using our cool HTML Helper class that is smart enough to render tables, data, HTML 5 elements etc and even bind to ColdFusion ORM entities.

    hashtag
    Views With No Layout

    So what happens if I DO NOT want the view to be rendered within a layout? Am I doomed? Of course not, just use the same method with the noLayout argument or event.noLayout() method:

    hashtag
    Views With Layouts

    If you need the view to be rendered in a specific layout, then use the layout argument or the setLayout() method:

    hashtag
    Views From Modules

    If you need the set a view to be rendered from a specific ColdBox Module then use the module argument alongside any other argument combination:

    hashtag
    Render Nothing

    You can also tell the renderer to not render back anything to the user by using the event.noRender() method. Maybe you just took some input and need to gracefully shutdown the request into the infamous white screen of death.

    hashtag
    Implicit Views

    You can also omit the explicit event.setView() if you want, ColdBox will then look for the view according to the executing event's syntax by convention. So if the incoming event is called general.index and no view is explicitly defined in your handler, ColdBox will look for a view in the general folder called index.cfm. That is why we recommend trying to match event resolution to view resolution even if you use or not implicit views.

    circle-check

    Tip: This feature is more for conventions purists than anything else. However, we do recommend as best practice to use explicitly declare the view to be rendered when working with team environments as everybody will know what happens.

    triangle-exclamation

    Caution If using implicit views, please note that the name of the view will ALWAYS be in lower case. So please be aware of this limitation. I would suggest creating URL Mappings with explicit event declarations so case and location can be controlled. When using implicit views you will also loose fine rendering control.

    hashtag
    Disabling Implicit Views

    You can also disable implicit views by using the coldbox.implicitViews configuration setting in your config/ColdBox.cfc. This is useful as implicit lookups are time-consuming.

    hashtag
    Case Sensitivity

    The ColdBox rendering engine can also be tweaked to use case-insensitive or sensitive implicit views by using the coldbox.caseSensitiveImplicitViews directive in your config/ColdBox.cfc. The default is to turn all implicit views to lower case, so the value is always false.

    <div id="leftbar">
    #runEvent( event='viewlets.userinfo', eventArguments={ userID=4 } )#
    </div>
    viewlets.cfc
    function userinfo( event, rc, prc, userID=0 ){
    
        // place data in prc and prefix it to avoid collisions
        prc.userinfo_qData = userService.getUserInfo( arguments.userID );
    
        // render out content 
        return renderView( "viewlets/userinfo" );
    }
    viewlets/userinfo.cfm
    <cfoutput>
        <div>User Info Panel</div>
        <div>Username: #prc.userinfo_qData.username#</div>
        <div>Last Login: #prc.userinfo_qData.lastLogin#</div>
    </cfoutput>
    function home(event,rc,prc){
    
        // render some content variables with funky arguments
        prc.sideColumn = renderView(view='tags/sideColumn',cache=true,cacheTimeout=10);
    
        // set view
        event.setView('general/home');
    }
    <div id="content">
      <div id="leftColumn">
      <cfoutput>#prc.sideColumn#</cfoutput>
      </div>
    
      <div id="mainView">
      <cfoutput>#renderView()#</cfoutput>
      </div>
    </div>
    <div id="content">
      <div id="leftColumn">
      <cfoutput>#prc.sideColumn ?: ''#</cfoutput>
      </div>
    
      <div id="mainView">
      <cfoutput>#renderView()#</cfoutput>
      </div>
    </div>
    handlers/main.cfc
    component
    {
    
        function index(event,rc,prc){
            // call some model for data and put into the request collection
            prc.myQuery = getInstance('MyService').getData();
            // set the view for rendering
            event.setView( "general/index" );
    
        }
    
    }
    main/index.cfm
    <cfoutput>
    <h1>My Cool Data</h1>
    #html.table( data=prc.myQuery, class="table table-striped table-hover" )#
    
    </cfoutput>
    component{
    
        function index(event,rc,prc){
            // call some model for data and put into the request collection
            prc.myQuery = getInstance('MyService').getData();
            // set the view for rendering
            event.setView( view="general/index", noLayout=true );
        }
    
        function index(event,rc,prc){
            // call some model for data and put into the request collection
            prc.myQuery = getInstance('MyService').getData();
            // set the view for rendering
            event.setView( "general/index" ).noLayout();
        }
    }
    component name="general"{
    
        function index(event,rc,prc){
            // call some model for data and put into the request collection
            prc.myQuery = getInstance('MyService').getData();
            // set the view for rendering
            event.setView( view="general/index", layout="Ajax" );
        }
    
        function index(event,rc,prc){
            // call some model for data and put into the request collection
            prc.myQuery = getInstance('MyService').getData();
            // set the view for rendering
            event.setView( "general/index" ).setLayout( "Ajax" );
        }
    
    }
    component name="general"{
    
        function index(event,rc,prc){
    
            // call some model for data and put into the request collection
            prc.myQuery = getInstance('MyService').getData();
            // set the view for rendering
            event.setView( view="general/index", module="shared-views" );
    
        }
    
    }
    component name="general"{
    
        function saveData(event,rc,prc){
            // do your work here …..
    
            // set for no render
            event.noRender();
        }
    
    }
    component name="general"{
    
        function index(event,rc,prc){
            // call some model for data and put into the request collection
            prc.myQuery = getInstance('MyService').getData();    
        }
    
    }
    coldbox.implicitViews = false;
    coldbox.caseSensitiveImplicitViews = true;
    so we need to be specific when getting timezones in the scheduler or else it fails

    COLDBOX-1016arrow-up-right CF-2018 stats.lastResult is null when task->call have runEvent("xxxx")

    COLDBOX-1015arrow-up-right Element cleaner not matching on query strings due to misordering of map keys. Use a TreepMap to normalize ordering so event caching and view caching cleanups can ocur.

    COLDBOX-1014arrow-up-right response object cached response was never used, rely on the master bootstrap ColdBox cache response header

    COLDBOX-1013arrow-up-right RendererEncapsulator overwrites view()-Method from FrameworkSupertype

    COLDBOX-1010arrow-up-right this.nullSupport = true breaks coldbox

    COLDBOX-1008arrow-up-right ScheduleTask listeners are only testing for isClosure() and lambdas return false, so do an or check for isCustomFunction() to support lambdas

    COLDBOX-1007arrow-up-right scheduled task doesn't listen to when() in Scheduler.cfc

    COLDBOX-1004arrow-up-right Custom matchers debug() function not found since it's injected in isolation

    COLDBOX-1003arrow-up-right Scheduler service DI constructor argument was using the string literal instead of the string value for the scheduler name

    COLDBOX-1002arrow-up-right added this scope to getTimezone() to address issue with ACF-2021 bif

    COLDBOX-1001arrow-up-right category not logged correctly in async loggers

    hashtag
    Improvements

    COLDBOX-1023arrow-up-right Readjustments to fail fast and reloading procedures when there are exceptions and reiniting becomes impossible.

    COLDBOX-1018arrow-up-right Enable proxy to use the ColdBox app key defined in the bootstrap instead of hard coding it

    COLDBOX-1017arrow-up-right Delay loadApplicationHelpers() in interceptors so modules can contribute udf helpers and even core interceptors can load them.

    COLDBOX-1012arrow-up-right Move coldbox inited flag to after after aspects load, so we can make sure all modules are loaded before serving requests

    COLDBOX-1009arrow-up-right RESTHandler capture JWT TokenException to authentication failures

    hashtag
    Bugs

    CACHEBOX-68arrow-up-right BlackHoleStore never finishes reap() method

    hashtag
    Improvements

    LOGBOX-63arrow-up-right Allow for dbappender to have default column maps instead of strict maps and allow for all methods to use the maps

    hashtag
    New Features

    Ability to add new appenders after config has been registered already

    COLDBOX-1024arrow-up-right
    COLDBOX-1024arrow-up-right
    COLDBOX-1026arrow-up-right
    COLDBOX-1025arrow-up-right
    COLDBOX-1021arrow-up-right
    COLDBOX-1020arrow-up-right
    COLDBOX-1019arrow-up-right
    COLDBOX-981arrow-up-right DataMarshaller no longer accepts 'row', 'column' or 'struct' as a valid argument.

    Improvements

    • COLDBOX-998arrow-up-right Convert util to script and optimize

    • COLDBOX-989arrow-up-right Add more debugging when exceptions occur when loading/unloading thread contexts

    • COLDBOX-971arrow-up-right Implement caching strategy for application helper lookups into the default cache instead of the template cache.

    New Features

    • COLDBOX-999arrow-up-right New SchedulerService that mointors and registers application scheduled tasks in an HMVC fashion

    • COLDBOX-997arrow-up-right Added out and error stream helpers to Scheduled Tasks for better debugging

    • COLDBOX-996arrow-up-right newTask() method on scheduled executor to replace nameless newSchedule

    • New scheduler object to keep track and metrics of registered tasks

    • New Scheduled Task with life-cycles and metrics

    • New async.time package to deal with periods, durations, time offsets and so much more

    • Added CFML Duration and Periods to async manager so task executions can be nicer and pin point accuracy

    • Allow structs for query strings when doing relocations

    • Encapsulate any type of exception in the REST Handler in a onAnyOtherException() action which can also be overidden by concrete handlers

    • Add registration and activation timestamps to the a module configuration object for active profiling.

    • Rename renderLayout() to just layout() and deprecate it for v7

    • Rename renderView() to just view() and deprecate it for v7

    hashtag
    Bugs

    • WIREBOX-112arrow-up-right virtual inheritance causes double inits on objects that do not have a constructor and their parent does.

    • onDIComplete() is called twice using virtual inheritance

    hashtag
    New Features

    • New coldbox dsl => coldbox:appScheduler which gives you the appScheduler@coldbox instance

    • new injection dsl: wirebox:asyncManager

    CFML Scheduled Tasks
    ColdBox Scheduled Tasks
    COLDBOX-991arrow-up-right
    COLDBOX-988arrow-up-right

    ElixirVueJS : A ColdBox Elixir + Vue.js based template

  • rest: A RESTFul services template

  • rest-hmvc: A RESTFul service built with modules

  • Simple : A traditional simple template

  • SuperSimple : The bare-bones template

  • https://packagecontrol.io/packages/ColdBox Platformarrow-up-right
    https://marketplace.visualstudio.com/items?itemName=ortus-solutions.vscode-coldboxarrow-up-right
    https://www.forgebox.io/view/ColdBox-Platform-Utilitiesarrow-up-right
    installarrow-up-right
    CommandBoxarrow-up-right
    https://www.ortussolutions.com/products/commandbox#downloadarrow-up-right
    https://commandbox.ortusbooks.com/setup/installationarrow-up-right
    www.forgebox.ioarrow-up-right
    your own
    github.com/coldbox-templatesarrow-up-right
    CommandBox Shell
    : The struct of extra arguments sent to an action if any
  • rc : The RC reference

  • prc : The PRC Reference

  • off
    or you will have to reinit the application in every request, which is
    annoying
    . Open the
    config/ColdBox.cfc
    and look for the
    coldbox.handlerCaching
    setting.

    By default this is already done for you on the application templates.

    hashtag
    Handler Code

    Go open the handlers/main.cfc and let's explore the code.

    Let's recap: Every action in ColdBox receives three arguments:

    • event - An object that models and is used to work with the current request, called the request context.

    • rc - A struct that contains both URL/FORM variables (unsafe data)

    • prc - A secondary struct that is private only settable from within your application (safe data)

    hashtag
    Setting Views - Default Layout

    This line event.setView( "main/index" ) in the index action told ColdBox to render a view back to the user found in views/main/index.cfm.

    ColdBox also has the concepts of layouts, which are essentially reusable views that can wrap other views or layouts. They allow you to reuse content so you can render views/layouts inside in a specific location in the CFML content. By convention, ColdBox looks for a layout called layouts/Main.cfm. This is yet another convention, the default layout. Your application can have many layouts or non layouts at all.

    hashtag
    Executing Events

    We have now seen how to add handlers via CommandBox using the coldbox create handler command and also execute them by convention by leveraging the following URL pattern:

    Also remember, that if no action is defined in the incoming URL then the default action of index will be used.

    circle-info

    Remember that the URL mappings support in ColdBox is what allows you to execute events in such a way from the URL. These are controlled by your application router: config/Router.cfc

    hashtag
    Working With Incoming Data

    Now, let's open the handler we created before called handlers/hello.cfc and add some public and private variables to it so our views can render the variables.

    Let's open the view now: views/hello/index.cfm and change it to this:

    triangle-exclamation

    Please note that we used the ColdFusion function encodeForHTML() (https://cfdocs.org/encodeforhtmlarrow-up-right) on the public variable. Why? Because you can never trust the client and what they send, make sure you use the built-in ColdFusion encoding functions in order to avoid XSS hacks or worse on incoming public (rc) variables.

    If you execute the event now: http://localhost:{port}/hello/index you will see a message of Hello nobody.

    Now change the incoming URL to this: http://localhost:{port}/hello/index?name=ColdBox and you will see a message of Hello ColdBox.

    circle-check

    Tip: Please see the layouts and views section for in-depth information about them.

    hashtag
    Creating A Service Model

    Let's create a simple contact listing, so open up CommandBox and issue the following command:

    This will create a models/ContactService.cfc with a getAll() method and a companion unit test at tests/specs/unit/ContactServiceTest.cfc. Let's open the model object:

    circle-exclamation

    Notice the singleton annotation on the component tag. This tells WireBox that this service should be cached for the entire application life-span. If you remove the annotation, then the service will become a transient object, which means that it will be re-created every time it is requested.

    hashtag
    Add Some Data

    Let's mock an array of contacts so we can display them later. We can move this to a SQL call later.

    circle-check

    We also have created a project to mock any type of data: MockDataCFCarrow-up-right. Just use CommandBox to install it: install mockdatacfc

    You can then leverage it to mock your contacts or any simple/complex data requirement.

    hashtag
    Wiring Up The Model To a Handler

    We have now created our model so let's tell our event handler about it. Let's create a new handler using CommandBox:

    This will create the handler/contacts.cfc handler with an index() action, the views/contacts/index.cfm view and the accompanying integration test tests/specs/integration/contactsTest.cfc.

    Let's open the handler and add a new ColdFusion property that will have a reference to our model object.

    Please note that inject annotation on the property definition. This tells WireBox what model to inject into the handler's variablesscope.

    By convention it looks in the models folder for the value, which in our case is ContactService. Now let's call it and place some data in the private request collection prc so our views can use it.

    hashtag
    Presenting The Data

    Now that we have put the array of contacts into the prc struct as aContacts, let's display it to the screen using ColdBox's HTML Helper.

    The ColdBox HTML Helper is a companion class that exists in all layouts and views that allows you to generate semantic HTML5 without the needed verbosity of nesting, or binding to ORM/Business objects.

    circle-info

    Please check out the API Docs to discover the HTML Helper: http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/modules/HTMLHelper/models/HTMLHelper.htmlarrow-up-right

    Open the contacts/index.cfm and add the following to the view:

    circle-exclamation

    Note: If your models are singletons, they will persist for the life-span of your ColdFusion application. To see code changes for singletons, you have to reinit the framework by using the ?fwreinit={password} Url action or via CommandBox using coldbox reinit. Please check out the API Docs to discover CommandBox: [https://apidocs.ortussolutions.com/commandbox/5.2.0/index.htmlarrow-up-right]

    That's it! Execute the event: http://localhost:{port}/contacts/index and view the nice table of contacts being presented to you.

    Congratulations, you have made a complete MVC circle!

    Tip You can find much more information about models and dependency injection in our full docsarrow-up-right

    modelarrow-up-right
    WireBoxarrow-up-right
    MVC
    Please visit the latest
    for further information about the request context.

    This object contains two structures internally:

    1. RC - The Request Collection which contains the FORM/REMOTE/URL data merged into a single structure. This is considered to be unsafe data as it comes from any request.

    2. PRC - The Private Request Collection which is a structure that can be used to safely store sensitive data. This structure cannot be modified from the outside world.

    circle-info

    The order of preference of variables when merged is FORM first then REMOTE then URL.

    REMOTE variables are from leveraging the ColdBox Proxy.

    You will use these objects in the controller and view layer of your application to get/set values, get metadata about the request, generate URLs, transform data for RESTful requests, and so much more. It is the glue that binds the controller and view layer together. As we progress in the guides, you will progress in mastering the request context.

    RC/PRC Data Super Highway
    triangle-exclamation

    Note that there is no model layer in the diagram. This is by design; the model will receive data from the handlers/interceptors directly.

    hashtag
    Most Commonly Used Methods

    Below you can see a listing of the most commonly used methods in the request context object. Please note that when interacting with a collection you usually have an equal private collection method.

    • buildLink() : Build a link in SES or non SES mode for you with tons of nice abstractions.

    • clearCollection() : Clears the entire collection

    • collectionAppend() : Append a collection overwriting or not

    • getCollection() : Get a reference to the collection

    • getEventName() : The event name in use in the application (e.g. do, event, fa)

    • getSelf() : Returns index.cfm?event=

    • getValue() : get a value

    • getTrimValue() : get a value trimmed

    • isProxyRequest() : flag if the request is an incoming proxy request

    • isSES() : flag if ses is turned on

    • isAjax() : Is this request ajax based or not

    • noRender(boolean) : flag that tells the framework to not render any html, just process and silently stop.

    • overrideEvent() : Override the event in the collection

    • paramValue(): param a value in the collection

    • removeValue() : remove a value

    • setValue() : set a value

    • setLayout() : Set the layout to use for this request

    • setView() : Used to set a view to render

    • valueExists() : Checks if a value exists in the collection.

    • renderData() : Marshall data to JSON, JSONP, XML, WDDX, PDF, HTML, etc.

    Some Samples:

    Please see the online API Docsarrow-up-right for the latest methods and arguments.

    hashtag
    Request Metadata Methods

    • getCurrentAction() : Get the current execution action (method)

    • getCurrentEvent() : Get the current incoming event, full syntax.

    • getCurrentHandler() : Get the handler or handler/package path.

    • getCurrentLayout() : Get the current set layout for the view to render.

    • getCurrentView() : Get the current set view

    • getCurrentModule() : The name of the current executing module

    • getCurrentRoutedNamespace() : The current routed URL mapping namespace if found.

    • getCurrentRouteRecord() : Get the current routed record used in resolving the event

    • getCurrentRouteMeta() : Get the current routed record metdata struct

    • getCurrentRoutedURL() : The current routed URL if matched.

    • getDefaultLayout() : Get the name of the default layout.

    • getDefaultView() : Get the name of the default view.

    Event Handler
    Action
    API Docsarrow-up-right
    circle-check

    Tip: Unlike previous versions of ColdBox, the new routing services in ColdBox 5 are automatically configured to detect the base URLs and support multi-domain hosting. There is no more need to tell the Router about your base URL.

    hashtag
    Application Router - Router.cfc

    The application router is a simple CFC that virtually inherits from the core ColdBox Router class and is configured via the configure() method. It will be decorated with all the capabilities to work with any request much like any event handler or interceptor. In this router you will be doing 1 of 2 things:

    1. Configuring the Router

    2. Adding Routes via the Routing DSL

    hashtag
    Router as an Interceptor

    The router and indeed all module routers are also registered as full fledged ColdBox interceptors. So they can listen to any event within your application.

    hashtag
    Generated Settings

    Once the routing service loads your Router it will create several application settings for you:

    • SesBaseUrl : The multi-domain URL base URL of your application: http://localhost

    • SesBasePath : The multi-domain path with no protocol or host

    • HtmlBaseUrl : The same path as SESBaseURLbut without any index.cfm in it (Just in case you are using index.cfm rewrite). This is a setting used most likely by the HTML <base> tag.

    • HtmlBasePath : Does not include protocol or host

    hashtag
    Configuration Methods

    You can use the following methods to fine tune the configuration and operation of the routing services:

    Method

    Description

    setEnabled( boolean )

    Enable/Disable routing, enabled by default

    setFullRewrites( boolean )

    If true, then no index.cfm will be used in the URLs. If false, then /index.cfm/ will be added to all generated URLs. Default is false.

    setUniqueURLS( boolean )

    Enables SES only URL's with permanent redirects for non-ses urls. Default is true. If true and a URL is detected with ? or & then the application will do a 301 Permanent Redirect and try to translate the URL to a valid SES URL.

    setBaseURL( string )

    The base URL to use for URL writing and relocations. This is automatically detected by ColdBox 5 e.g. , ''

    setLooseMatching( boolean )

    By default URL pattern matching starts at the beginning of the URL, however, you can choose loose matching so it searches anywhere in the URL. Default is false.

    The next sections will discus how to register routes for your application.

    API Docsarrow-up-right

    Directive

    Type

    Description

    struct

    An optional structure used to configure CacheBox. If not setup the framework will use its default configuration found in /coldbox/system/web/config/CacheBox.cfc

    struct

    The main coldbox directives structure that holds all the coldbox settings.

    struct

    A structure where you will configure the application convention names

    struct

    A structure where you will configure environment detection patterns

    struct

    A structure where you will configure the

    struct

    An optional structure to configure application wide interceptor behavior

    array

    An optional array of interceptor declarations for your application

    struct

    A structure where you define how the layout manager behaves in your application

    array

    An array of layout declarations for implicit layout-view-folder pairings in your application

    struct

    An optional structure to configure the logging and messaging in your application via LogBox

    struct

    An optional structure to configure application wide module behavior

    struct

    An optional structure to configure individual modules installed in your application.

    struct

    A structure where you can put your own application settings

    struct

    An optional structure used to define how WireBox is loaded

    controller

    A reference to the application's ColdBox Controller (coldbox.system.web.Controller)

    flash

    A reference to the current configured Flash Object Implementation that inherits from the AbstractFlashScope AbstractFlashScopearrow-up-right (derived coldbox.system.web.flash.AbstractFlashScope)

    logbox

    The reference to the LogBoxarrow-up-right library (coldbox.system.logging.LogBox)

    log

    A pre-configured LogBox Loggerarrow-up-right object for this specific class object (coldbox.system.logging.Logger)

    wirebox

    A reference to the WireBoxarrow-up-right object factory (coldbox.system.ioc.Injector)

    HTML Helper
    CacheBoxarrow-up-right
    https://packagecontrol.io/packages/ColdBox Platformarrow-up-right
    https://marketplace.visualstudio.com/items?itemName=ortus-solutions.vscode-coldboxarrow-up-right
    https://www.forgebox.io/view/ColdBox-Platform-Utilitiesarrow-up-right
    installarrow-up-right
    CommandBoxarrow-up-right
    https://www.ortussolutions.com/products/commandbox#downloadarrow-up-right
    https://commandbox.ortusbooks.com/setup/installationarrow-up-right
    www.forgebox.ioarrow-up-right
    next section
    github.com/coldbox-templatesarrow-up-right
    CommandBox Shell

    Bootstrapper - Application.cfc

    The Application.cfc is one of the most important files in your application as it is where you define all the implicit ColdFusion engine events, session, client scopes, ORM, etc. It is also how you tell ColdFusion to bootstrap the ColdBox Platform for your application. There are two ways to bootstrap your application:

    1. Leverage composition and bootstrap ColdBox (Default)

    2. Leverage inheritance and bootstrap ColdBox

    circle-info

    The composition approach allows you to have a more flexible configuration as it will allow you to use per-application mappings for the location of the ColdBox Platform.

    circle-check

    Tip: To see the difference, just open the appropriate Application.cfc in the application templates.

    hashtag
    Composition

    hashtag
    Inheritance

    hashtag
    Directives

    You can set some variables in the Application.cfc that can alter Bootstrapping conditions:

    hashtag
    Lock Timeout

    The Boostrapper also leverages a default locking timeout of 30 seconds when doing loading operations. You can modify this timeout by calling the setLockTimeout() method on the Bootsrapper object.

    Rewrite Rules

    Here are just a few of those rewrite rules for you for major rewrite engines. You can spice them up as needed.

    hashtag
    .htaccess

    circle-exclamation

    The above htaccess file might not work combined with Apache. Recent versions of Apache don't send the CGI.PATH_INFO variable to your cfml engine when using ProxyPass and ProxyPassMatch. It that's the case you might need a function in your router.cfc

    The following solution might work better if you are using a recent version of Apache. This should be part of your .htaccess file

    Now you can create a pathInfo provider function in your router.cfc which brings back your path info to the router:

    hashtag
    nginx

    hashtag
    IIS7 web.config

    Please note that URL rewriting is handled by an optional module in IIS. More info here:

    hashtag
    Tuckey Rewrite

    Event Caching

    Event caching is extremely useful and easy to use. ColdBox will act like a cache proxy between your events and the clients requesting the events, much like squid, nginx or HA Proxy. All you need to do is add several metadata arguments to the action methods and the framework will cache the output of the event in the template cache provider in CacheBox. In other words, the event executes and produces output that the framework then caches. Subsequent calls to the same event with the same incoming RC variables will not do any processing, but just output the content back to the user.

    For example, you have an event called blog.showEntry. This event executes, gets an entry from the database and sets a view to be rendered. The framework then renders the view and if event caching is turned on for this event, the framework will cache the HTML produced. So the next incoming show entry event will just spit out the cached HTML. The cache key is created by hashing the incoming request collection.

    circle-info

    Important to note also, that any combination of URL/FORM parameters on an event will produce a unique cacheable key. So event=blog.showEntry&id=1 & event=blog.showEntry&id=2 are two different cacheable events.

    hashtag
    Enabling Event Caching

    To enable event caching, you will need to set a setting in your ColdBox.cfc called coldbox.eventcaching to true.

    triangle-exclamation

    Important Enabling event caching does not mean that ALL events will be cached. It just means that you enable this feature.

    hashtag
    Setting Up Actions For Caching

    The way to set up an event for caching is on the function declaration with the following annotations:

    triangle-exclamation

    Important Please be aware that you should not cache output with 0 timeouts (forever). Always use a timeout.

    triangle-exclamation

    Alert: DO NOT cache events as unlimited timeouts. Also, all events can have an unlimited amount of permutations, so make sure they expire and you purge them constantly. Every event + URL/FORM variable combination will produce a new cacheable entry.

    hashtag
    Storage

    All event and view caching are stored in a named cache called template which all ColdBox applications have by default. You can open or create a new configuration object and decide where the storage is, timeouts, providers, etc. You have complete control of how event and view caching is stored.

    hashtag
    Purging

    We also have a great way to purge these events programmatically via our cache provider interface.

    Methods for event purging:

    • clearEvent( string eventSnippet, string querystring="" ): Clears all the event permutations from the cache according to snippet and querystring. Be careful when using incomplete event name with query strings as partial event names are not guaranteed to match with query string permutations

    • clearEventMulti( eventsnippets,string querystring="" ): Clears all the event permutations from the cache according to the list of snippets and querystrings. Be careful when using incomplete event name with query strings as partial event names are not guaranteed to match with query string permutations

    hashtag
    this.event_cache_suffix

    You can now leverage the cache suffix property in handlers to be declared as a closure so it can be evaluated at runtime so it can add dynamic suffixes to cache keys. This can allow you to incorporate elements into the cache key at runtime instead of statically. This is a great way to incorporate the user's language locale or session identifier to make unique entries.

    hashtag
    OnRequestCapture - Influence Cache Keys

    We have provided an interception point in ColdBox that allows you to add variables into the request collection before a snapshot is made so you can influence the cache key of a cacheable event. What this means is that you can use it to mix in variables into the request collection that can make this event cache unique for a user, a specific language, country, etc. This is a great way to leverage event caching on multi-lingual or session based sites.

    With the simple example above, the user's locale will be added to all your event caching permutations and thus create entries for different languages.

    hashtag
    Life-Cycle Caveats

    triangle-exclamation

    Several event interception points are NOT available during event caching.

    When using event caching the framework will NOT execute ANY event at all. It will stream the content directly from the selected cache provider. This means that any interceptors or code that executes in the event is also NOT executed. The only interception points that will execute are:

    • preProcess

    • postProcess

    So please make sure you take note of this when planning for event security.

    hashtag
    Monitoring

    has an intuitive and powerful monitor that can be used via the ColdBox Debugger Module. From the monitor you can purge, expire and view cache elements, etc.

    ColdBox

    The ColdBox directive is where you configure the framework for operation.

    hashtag
    Application Setup

    Info : Please note that there are no mandatory settings as of ColdBox 4.2.0. If fact, you can remove the config file completely and your app will run. It will be impossible to reinit the app however without a reinit password set.

    Event Handlers

    Event handlers are ColdBox's version of controllers in the MVC design pattern. So every time you hear "event handler", you are talking about a controller that can listen to external events or internal events in ColdBox. Event handlers are responsible for controlling your application flow, calling business logic, preparing a display to a user and much more.

    hashtag
    Locations

    All your handlers will be stored in the handlers folder of your application template. If you get to the point where your application needs even more decoupling and separation, please consider building

    Routines cannot be declared more than once.
    The routine auth has been declared twice in different templates.
    
    ColdFusion cannot determine the line of the template that caused this error. This is often caused by an error in the exception handling subsystem.
    mkdir myapp --cd
    install coldbox
    Dir 0 Apr 25,2018 11:04:05 coldbox
    File 112 Apr 25,2018 11:04:05 box.json
    // executes around any action
    function aroundHandler(event,targetAction,eventArguments,rc,prc){
    }
    
    // executes around the list() action ONLY
    function aroundList(event,targetAction,eventArguments,rc,prc){
    }
    
    // Around handler advice for transactions
    function aroundHandler(event,targetAction,eventArguments,rc,prc){
    
        // log the call
        log.debug("Starting to execute #targetAction.toString()#" );
    
        // start a transaction
        transaction{
    
            // prepare arguments for action call
            var args = {
                event = arguments.event,
                rc    = arguments.rc,
                prc   = arguments.prc
    
            };
            structAppend( args, eventArguments );
            // execute the action now
            var results = arguments.targetAction( argumentCollection=args );
        }
    
        // log the call
        log.debug( "Ended executing #targetAction.toString()#" );
    
        // return if it exists
        if( !isNull( results ) ){ return results; }
    }
    
    // Around handler advice for try/catches
    function aroundHandler(event,targetAction,eventArguments,rc,prc){
    
        // log the call
        if( log.canDebug() ){
            log.debug( "Starting to execute #targetAction.toString()#" );
        }
    
        // try block
        try{
    
            // prepare arguments for action call
            var args = {
                event = arguments.event,
                rc    = arguments.rc,
                prc   = arguments.prc
    
            };
            structAppend( args, eventArguments );
            // execute the action now
            return arguments.targetAction( argumentCollection=args );
        }
        catch(Any e){
            // log it
            log.error("Error executing #targetAction.toString()#: #e.message# #e.detail#", e);
            // set exception in request collection and set view to render
            event.setValue( "exception", e)
                .setView( "errors/generic" );
    
        }
    
    }
    // only fire for the actions: save(), delete()
    this.aroundhandler_only = "save,delete";
    // DO NOT fire for the actions: login(), doLogin(), logout()
    this.aroundhandler_except = "login,doLogin,logout"
    config/Coldbox.cfc
    /**
     * Development environment
     */
    function development() {
        coldbox.customErrorTemplate = "/coldbox/system/exceptions/Whoops.cfm"; // interactive bug report
        coldbox.handlerCaching = false;
        coldbox.handlersIndexAutoReload = true;
        coldbox.eventCaching = false;
        coldbox.viewCaching = false;
    }
    component extends="coldbox.system.EventHandler" {
    
        /**
         * Default Action
         */
        function index( event, rc, prc ) {
            prc.welcomeMessage = "Welcome to ColdBox!";
            event.setView( "main/index" );
        }
    
        /**
         * Produce some restfulf data
         */
        function data( event, rc, prc ) {
            return [
                { "id" : createUUID(), name : "Luis" },
                { "id" : createUUID(), name : "JOe" },
                { "id" : createUUID(), name : "Bob" },
                { "id" : createUUID(), name : "Darth" }
            ];
        }
    
        /**
         * Relocation example
         */
        function doSomething( event, rc, prc ) {
            relocate( "main.index" );
        }
    
        /************************************** IMPLICIT ACTIONS *********************************************/
    
        function onAppInit( event, rc, prc ) {
        }
    
        function onRequestStart( event, rc, prc ) {
        }
    
        function onRequestEnd( event, rc, prc ) {
        }
    
        function onSessionStart( event, rc, prc ) {
        }
    
        function onSessionEnd( event, rc, prc ) {
            var sessionScope     = event.getValue( "sessionReference" );
            var applicationScope = event.getValue( "applicationReference" );
        }
    
        function onException( event, rc, prc ) {
            event.setHTTPHeader( statusCode = 500 );
            // Grab Exception From private request collection, placed by ColdBox Exception Handling
            var exception = prc.exception;
            // Place exception handler below:
        }
    
    }
    http://localhost:{port}/folder/handler/action
    http://localhost:{port}/handler/action
    http://localhost:{port}/handler
    function index( event, rc, prc ){
        // param an incoming variable.
        event.paramValue( "name", "nobody" );
        // set a private variable
        prc.when = dateFormat( now(), "full" );
        // set the view to render
        event.setView( "hello/index" );
    }
    <cfoutput>
    <p>Hello #encodeForHTML( rc.name )#, today is #prc.when#</p>
    </cfoutput>
    coldbox create model name="ContactService" methods="getAll" persistence="singleton"
    /**
     * I am a new Model Object
     */
    component singleton accessors="true"{
    
    	// Properties
    	
    
    	/**
    	 * Constructor
    	 */
    	ContactService function init(){
    
    		return this;
    	}
    
    	/**
    	 * getAll
    	 */
    	function getAll(){
    
    	}
    
    
    }
    /**
     * I am a new Model Object
     */
    component singleton accessors="true"{
    
    	// Properties
    	property name="data" type="array";
    
    	/**
    	 * Constructor
    	 */
    	ContactService function init(){
    	  variables.data = [
                { "id"=1, "name"="coldbox" },
                { "id"=2, "name"="superman" },
                { "id"=3, "name"="batman" }
              ];
    		return this;
    	}
    
    	/**
    	 * Get all the contacts
    	 */
    	function getAll(){
    	  return variables.data;
    	}
    
    
    }
    coldbox create handler name="contacts" actions="index"
    component{ 
    
        property name="contactService" inject="ContactService";
    
        any function index( event, rc, prc ){ 
            event.setView( "contacts/index" ); 
        }
    }
    any function index( event, rc, prc ){
        prc.aContacts = contactService.getAll();
        event.setView( "contacts/index" );
    }
    <cfoutput>
    <h1>My Contacts</h1>
    
    #html.table( data=prc.aContacts, class="table table-striped" )#
    </cfoutput>
    // test if this is an MVC request or a remote request
    if ( event.isProxyRequest() ){
      event.setValue('message', 'We are in proxy mode right now');
    }
    
    // param a variable called page
    event.paramValue('page',1);
    // then just use it
    event.setValue('link','index.cfm?page=#rc.page#');
    
    // get a value with a default value
    event.setvalue('link','index.cfm?page=#event.getValue('page',1)#');
    
    // Set the view to render
    event.setView('homepage');
    
    // Set the view to render with no layout
    event.setView('homepage',true);
    
    // set the view to render with caching stuff
    event.setview(name='homepage',cache='true',cacheTimeout='30');
    
    // override a layout
    event.setLayout('Layout.Ajax');
    
    // check if a value does not exists
    if ( !event.valueExists('username') ) {
    
    }
    
    // Tell the framework to stop processing gracefully, no renderings
    event.noRender();
    
    // Build a link
    <form action="#event.buildLink( 'user.save' )#" method="post">
    </form>
    config/Router.cfc
    component {
    
    	function configure() {
    		// Set Full Rewrites
    		setFullRewrites( true );
    
    		/**
    		 * --------------------------------------------------------------------------
    		 * App Routes
    		 * --------------------------------------------------------------------------
    		 *
    		 * Here is where you can register the routes for your web application!
    		 * Go get Funky!
    		 *
    		 */
    
    		// A nice healthcheck route example
    		route( "/healthcheck", function( event, rc, prc ) {
    			return "Ok!";
    		} );
    
    		// A nice RESTFul Route example
    		route( "/api/echo", function( event, rc, prc ) {
    			return {
    				"error" : false,
    				"data"  : "Welcome to my awesome API!"
    			};
    		} );
    
    		route(
    			pattern : "/api/contacts",
    			target  : "contacts.index",
    			name    : "api.contacts"
    		);
    
    		// Conventions based routing
    		route( ":handler/:action?" ).end();
    	}
    
    }
    
    function configure(){
    
        setFullRewrites( true );
        setExtensionDetection( true );
        setValidExtensions( "json,cfm,pdf" );
        setMultiDomainDiscovery( false )
    
    }
    ColdBox.cfc
    /**
    * A simple CFC that configures a ColdBox application.  You can even extend, compose, strategize and do your OO goodness.
    */
    component{
    
        // Mandatory configuration method
        function configure(){
            coldbox = {
              
            };
        }
        
    }
    mkdir 60-minute-quickstart --cd
    install coldbox
    Dir 0 Jan 25,2021 11:04:05 coldbox
    File 112 Jan 25,2021 11:04:05 box.json
    RewriteEngine on
    #if this call related to adminstrators or non rewrite folders, you can add more here.
    RewriteCond %{REQUEST_URI} ^/(.*(CFIDE|cfide|CFFormGateway|jrunscripts|railo-context|lucee|mapping-tag|fckeditor)).*$
    RewriteRule ^(.*)$ - [NC,L]
    
    #Images, css, javascript and docs, add your own extensions if needed.
    RewriteCond %{REQUEST_URI} \.(bmp|gif|jpe?g|png|css|js|txt|xls|ico|swf)$
    RewriteRule ^(.*)$ - [NC,L]
    
    #The ColdBox index.cfm/{path_info} rules.
    RewriteRule ^$ index.cfm [QSA,NS]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ index.cfm/%{REQUEST_URI} [QSA,L,NS]
    LOGBOX-64arrow-up-right

    setMultiDomainDiscovery( boolean )

    Defaults to true. With this setting on, every request will be inspected for the incoming host for usage in building links and domain detection.

    setExtensionDetection( boolean )

    By default ColdBox detects URL extensions like json, xml, html, pdf which can allow you to build awesome RESTful web services. Default is true.

    setValidExtensions( list )

    Tell the interceptor what valid extensions your application can listen to. By default it listens to: json, jsont, xml, cfm, cfml, html, htm, rss, pdf

    setThrowOnInvalidExtensions( boolean )

    By default ColdBox does not throw an exception when an invalid extension is detected. If true, then the interceptor will throw a 406 Invalid Requested Format Extension: {extension} exception. Default is false.

    http://www.coldbox.org/arrow-up-right
    http://mysite.com/index.cfmarrow-up-right
    cachebox
    coldbox
    conventions
    environments
    flash
    FlashRAMarrow-up-right
    interceptorSettings
    interceptors
    layoutSettings
    layouts
    logbox
    modules
    moduleSettings
    settings
    wirebox
    pathInfoProvider
    https://www.iis.net/downloads/microsoft/url-rewritearrow-up-right
    hashtag
    Development Settings

    hashtag
    reinitPassword

    Protect the reinitialization of the framework URL actions. For security, if this setting is omitted, we will create a random password. Setting it to an empty string will allow you to reinitialize without a password. Always have a password set for public-facing sites.

    hashtag
    reinitKey

    The key used in FORM or URL to reinit the framework. The default is fwreinit but you can change it to whatever you like.

    hashtag
    handlersIndexAutoReload

    Will scan the conventions directory for new handler CFCs on each request if activated. Use false for production, this is only a development true setting.

    hashtag
    Implicit Event Settings

    These settings map 1-1 from ColdBox events to the Application.cfc life-cycle methods. The only one that is not is the defaultEvent, which selects what event the framework will execute when no incoming event is detected via URL/FORM or REMOTE executions.

    hashtag
    Extension Points

    The ColdBox extension points are a great way to create federated applications that can reuse a centralized core instead of the local conventions. It is also a great way to extend some core classes with your own.

    hashtag
    applicationHelper

    A list or array of absolute or relative paths to a UDF helper file. The framework will load all the methods found in this helper file globally. Meaning it will be injected in ALL handlers, layouts and views.

    hashtag
    viewsHelper

    A list or array of absolute or relative paths to a UDF helper file. The framework will load all the methods found in this helper in layouts and views only.

    hashtag
    modulesExternalLocation

    A list or array of locations of where ColdBox should look for modules to load into your application. The path can be a cf mapping or cfinclude compatible location. Modules are searched and loaded in the order of the declared locations. The first location ColdBox will search for modules is the conventions folder modules

    hashtag
    viewsExternalLocation

    The CF include path of where to look for secondary views for your application. Secondary views look just like normal views except the framework looks in the conventions folder first and if not found then searches this location.

    hashtag
    layoutsExternalLocation

    The CF include path of where to look for secondary layouts for your application. Secondary layouts look just like normal layouts except the framework looks in the conventions folder first and if not found then searches this location.

    hashtag
    handlersExternalLocation

    The CF dot notation path of where to look for secondary events for your application. Secondary events look just like normal events except the framework looks in the conventions folder first and if not found then searches this location.

    hashtag
    requestContextDecorator

    The CF dot notation path of the CFC that will decorate the system Request Context object.

    hashtag
    controllerDecorator

    The CF dot notation path of the CFC that will decorate the system Controller

    hashtag
    Exception Handling

    hashtag
    exceptionHandler

    The event handler to call whenever ANY non-catched exception occurs anywhere in the request lifecycle execution. Before this event is fired, the framework will log the error and place the exception in the prc as prc.exception.

    hashtag
    invalidHTTPMethodHandler

    The event handler to call whenever a route or event is accessed with an invalid HTTP method.

    hashtag
    invalidEventHandler

    This is the event handler that will fire masking a non-existent event that gets requested. This is a great place to place 302 or 404 redirects whenever non-existent events are being requested.

    hashtag
    customErrorTemplate

    The relative path from the application's root level of where the custom error template exists. This template receives a key in the private request collection called exception that contains the exception. By default ColdBox does not show robust exceptions, you can turn on robust exceptions by choosing the following template:

    hashtag
    Application Aspects

    hashtag
    autoMapModels

    ColdBox by convention can talk to, use and inject models from the models folder by just using their name. On startup it will scan your entire models folder and will register all the discovered models. This setting is true by default.

    hashtag
    caseSensitiveImplicitViews

    By default implicit views are case sensitive since ColdBox version 5.2.0, before this version the default was false.

    hashtag
    eventCaching

    This directive tells ColdBox that when events are executed they will be inspected for caching metadata. This does not mean that ALL events WILL be cached if this setting is turned on. It just activates the inspection mechanisms for whenever you annotate events for caching or using the runEvent() caching methods.

    hashtag
    handlerCaching

    This is useful to be set to false in development and true in production. This tells the framework to cache your event handler objects as singletons.

    hashtag
    implicitViews

    Allows you to use implicit views in your application and view dispatching. You can get a performance boost if you disable this setting.

    hashtag
    identifierProvider

    This setting allows you to configure a lambda/closure that will return back the user's request identifier according to your own algorithms. This overrides the internal way ColdBox identifies requests incoming to the application which are used internally to track sessions, flash rams, etc.

    The discovery algorithm we use is the following:

    1. If we have an identifierProvider closure/lambda/udf, then call it and use the return value

    2. If we have sessions enabled, use the jessionId or session URL Token

    3. If we have cookies enabled, use the cfid/cftoken

    4. If we have in the URL the cfid/cftoken

    5. Create a request based tracking identifier: cbUserTrackingId

    hashtag
    proxyReturnCollection

    This is a boolean setting used when calling the ColdBox proxy's process() method from a Flex or SOAP/REST call. If this setting is set to true, the proxy will return back to the remote call the entire request collection structure ALWAYS! If set to false, it will return, whatever the event handler returned back. Our best practice is to always have this false and return appropriate data back.

    hashtag
    viewCaching

    This directive tells ColdBox that when views are rendered, the cache=true parameter will be obeyed. Turning on this setting will not cause any views to be cached unless you are also passing in the caching parameters to your renderView() or event.setView() calls.

    #The ColdBox index.cfm/{path_info} rules.
    RewriteEngine On
    RewriteRule ^$ /index.cfm?redirect_path=/ [QSA,NS]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /index.cfm?redirect_path=%{REQUEST_URI} [QSA,L,NS]
    router.cfc
    function PathInfoProvider( event ){
      var p = cgi.path_info;
      if (len(p)) {
        return p;
      } else if (url.keyExists("redirect_path")) {
        return url.redirect_path;
      } else {
        return "";
      }
    }
    ################### LOCATION: ROOT #####################
    location / {
         # First attempt to serve real files or directory, else it sends it to the @rewrite location for processing
         try_files $uri $uri/ @rewrite;
    }
    
    ################### @REWRITE: COLDBOX SES RULES #####################
    # Rewrite for ColdBox (only needed if you want SES urls with this framework)
    # If you don't use SES urls you could do something like this
    # location ~ \.(cfm|cfml|cfc)(.*)$ {
    location @rewrite {
      rewrite ^/(.*)? /index.cfm/$request_uri last;
      rewrite ^ /index.cfm last;
    }
    
    ################### CFM/CFC LUCEE HANDLER #####################
    # The above locations will just redirect or try to serve cfml files
    # We need this to tell NGinx that if we receive the following requests to pass them to Lucee
    location ~ \.(cfm|cfml|cfc|jsp)(.*)$ {
    # Include our connector
    include lucee.conf;
    }
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <system.webServer>
            <rewrite>
                <rules>
                   <rule name="Application Administration" stopProcessing="true">
                        <match url="^(.*)$" />
                        <conditions logicalGrouping="MatchAll">
                            <add input="{SCRIPT_NAME}" pattern="^/(.*(CFIDE|cfide|CFFormGateway|jrunscripts|lucee|railo-context|fckeditor)).*$" ignoreCase="false" />
                        </conditions>
                        <action type="None" />
                    </rule>
                    <rule name="Flash and Flex Communication" stopProcessing="true">
                        <match url="^(.*)$" ignoreCase="false" />
                        <conditions logicalGrouping="MatchAll">
                            <add input="{SCRIPT_NAME}" pattern="^/(.*(flashservices|flex2gateway|flex-remoting)).*$" ignoreCase="false" />
                        </conditions>
                        <action type="Rewrite" url="index.cfm/{PATH_INFO}" appendQueryString="true" />
                    </rule>
                    <rule name="Static Files" stopProcessing="true">
                        <match url="^(.*)$" />
                        <conditions logicalGrouping="MatchAll">
                            <add input="{SCRIPT_NAME}" pattern="\.(bmp|gif|jpe?g|png|css|js|txt|pdf|doc|xls)$" ignoreCase="false" />
                        </conditions>
                        <action type="None" />
                    </rule>
                    <rule name="Insert index.cfm" stopProcessing="true">
                        <match url="^(.*)$" ignoreCase="false" />
                        <conditions logicalGrouping="MatchAll">
                            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                        </conditions>
                        <action type="Rewrite" url="index.cfm/{PATH_INFO}" appendQueryString="true" />
                    </rule>
                </rules>
            </rewrite>
        </system.webServer>
    </configuration>
    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN" "http://tuckey.org/res/dtds/urlrewrite3.2.dtd">
    <urlrewrite>
        <rule>
            <note>ContentBox Media URLs</note>
            <condition type="request-uri" operator="equal">^/__media/.*$</condition>
            <from>^/(.*)$</from>
            <to type="passthrough">/index.cfm/$1</to>
        </rule>
        <rule>
            <note>Generic Front-Controller URLs</note>
            <condition type="request-uri" operator="notequal">/(index.cfm|robots.txt|osd.xml|flex2gateway|cfide|cfformgateway|railo-context|lucee|admin-context|modules/contentbox-dsncreator|modules/contentbox-installer|modules/contentbox|files|images|js|javascripts|css|styles|config).*</condition>
            <condition type="request-uri" operator="notequal">\.(bmp|gif|jpe?g|png|css|js|txt|xls|ico|swf|woff|ttf|otf)$</condition>
            <condition type="request-filename" operator="notdir"/>
            <condition type="request-filename" operator="notfile"/>
            <from>^/(.+)$</from>
            <to type="passthrough">/index.cfm/$1</to>
        </rule>
    </urlrewrite>
    coldbox = {
        // The name of the application
        appName     = "My App",
        // The name of the incoming URL/FORM/REMOTE variable that tells the framework what event to execute. Ex: index.cfm?event=users.list
        eventName   = "event",
        // The URI of the ColdBox application on the webserver. Use when ColdBox app exists within subdirectory from project root, otherwise can be omitted
        appMapping  = ""
    };
    coldbox = {
        reinitPassword = "h1cker",
        reinitKey = "fwReinit",
        handlersIndexAutoReload = true
    };
    // reinit with no password
    http://localhost/?fwreinit=1
    // reinit with password
    http://localhost/?fwreinit=mypass
    coldbox = {
        reinitKey = "myreinit"
    }
    coldbox={
        //Implicit Events
        defaultEvent  = "Main.index",
        requestStartHandler     = "Main.onRequestStart",
        requestEndHandler   = "Main.onRequestEnd",
        applicationStartHandler = "Main.onAppInit",
        applicationEndHandler = "Main.onAppEnd",
        sessionStartHandler = "Main.onSessionStart",
        sessionEndHandler = "Main.onSessionEnd",
        missingTemplateHandler = "Main.onMissingTemplate"
    }
    coldbox={
        //Extension Points
        applicationHelper             = "includes/helpers/ApplicationHelper.cfm",
        viewsHelper                    = "",
        modulesExternalLocation        = [],
        viewsExternalLocation        = "",
        layoutsExternalLocation     = "",
        handlersExternalLocation      = "",
        requestContextDecorator     = "",
        controllerDecorator         = ""
    }
    coldbox = {
        // Error/Exception Handling handler
        exceptionHandler        = "",
        // Invalid HTTP method Handler
        invalidHTTPMethodHandler = "",
        // The handler to execute on invalid events
        invalidEventHandler = "",
        // The default error template    
        customErrorTemplate     = "/coldbox/system/includes/BugReport-Public.cfm"
    }
    coldbox.customErrorTemplate = "/coldbox/system/includes/BugReport.cfm";
    coldbox = {
        // Persist handlers
        handlerCaching             = false,
        // Activate event caching
        eventCaching            = true,
        // Activate view  caching
        viewCaching              = true,
        // Return RC struct on Flex/Soap Calls
        proxyReturnCollection     = false,
        // Activate implicit views
        implicitViews           = true,
        // Case for implicit views
        caseSensitiveImplicitViews = true,
        // Auto register all model objects in the `models` folder into WireBox
        autoMapModels     = true,
        // Your very own session tracking identifier
        identifierProvider = function(){
            return my own session tracking id;
        }
    }

    The name of the key the framework will store the application controller under in the application scope.

    COLDBOX_FAIL_FAST

    true

    By default if an app is reiniting and a request hits it, we will fail fast with a message. This can be a boolean indicator or a closure.

    Variable

    Default

    Description

    COLDBOX_APP_ROOT_PATH

    App Directory

    Automatically set for you. This path tells the framework what is the base root location of your application and where it should start looking for all the agreed upon conventions. You usualy will never change this, but you can.

    COLDBOX_APP_MAPPING

    /

    The application mapping is ESSENTIAL when dealing with Flex or Remote (SOAP) applications. This is the location of the application from the root of the web root. So if your app is at the root, leave this setting blank. If your application is embedded in a sub-folder like MyApp, then this setting will be auto-calculated to /MyApp.

    COLDBOX_CONFIG_FILE

    config/ColdBox.cfc

    The absolute or relative path to the configuration CFC file to load. This bypasses the conventions and uses the configuration file of your choice.

    COLDBOX_APP_KEY

    cbController

    The cache provider to store the results in. By default it uses the template cache.

    clearAllEvents( [boolean async=true] ) : Can clear ALL cached events in one shot and can be run asynchronously.

    Annotation

    Type

    Description

    cache

    boolean

    A true or false will let the framework know whether to cache this event or not. The default is FALSE. So setting to false makes no sense

    cachetimeout

    numeric

    The timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default object timeout in the cache settings. You can place a 0 in order to tell the framework to cache the event's output for the entire application timeout controlled by coldfusion, NOT GOOD. Always set a decent timeout for content.

    cacheLastAccesstimeout

    numeric

    The last access timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default last access object timeout in the cache settings. This tells the framework that if the object has not been accessed in X amount of minutes, then purge it.

    cacheProvider

    CacheBoxarrow-up-right
    CacheBoxarrow-up-right

    string

    instead.
    circle-info

    Tip: You can create packages or sub-folders inside of the handlers directory. This is encouraged on large applications so you can organize or package handlers logically to facilitate better maintenance and URL experience.

    hashtag
    External Location

    You can also declare a HandlersExternalLocation directive in your Configuration CFC. This will be a dot notation path or instantiation path where more external event handlers can be found.

    circle-exclamation

    If an external event handler has the same name as an internal conventions event, the internal conventions event will take precedence.

    hashtag
    Development Settings

    By default, ColdBox will only scan for event handlers on startup. For development we highly encourage you leverage the following configuration directives:

    hashtag
    Anatomy

    Event handlers are CFCs that will respond to FORM posts, HTTP requests and/or remote requests (like Flex,Air, SOAP, REST) via an incoming RC variable called event or by URL mappings (Which we saw in the previous section).

    hashtag
    Components

    circle-info

    You can also remove the inheritance from the CFC and WireBox will extend the coldbox.system.EventHandler for you using Virtual Inheritancearrow-up-right.

    circle-info

    Event Handlers are treated as singletons by ColdBox, so make sure you make them thread-safe and properly scoped. Persistence is controlled by the coldbox.handlerCaching directive

    hashtag
    Actions

    They are composed of functions which are called actions that will always have the following signature:

    Each action receives three arguments:

    1. event - An object that models and is used to work with the current request (otherwise known as the Request Context)

    2. rc - A struct that contains both URL/FORM variables (unsafe data)

    3. prc - A secondary struct that is private. This structure is only accessible from within your application (safe data)

    An action will usually do one of the following:

    • Set a view/layout to render

    • Return HTML

    • Return Complex Data which is converted to JSON by default

    • Relocate to another event/URL Route

    The default action for all event handlers is called index(). This means that when you execute an event, you can omit the index if you so desire.

    hashtag
    Private Actions

    So what about private functions? Private functions are not executable from the outside world, but can be executed internally via a function available to all handlers called runEvent(), which we will explore later.

    hashtag
    Composed Properties

    It is important to note that there is a pre-defined object model behind every event handler controller that will enable you to do your work more efficiently. The following are the automatically generated properties every event handler makes available in their variables scope:)

    Event Handler UML
    • cachebox : A reference to the CacheBox arrow-up-rightlibrary (coldbox.system.cache.CacheFactory)

    • controller : A reference to the Application Controller (coldbox.system.web.Controller)

    • flash: A flash memory object (coldbox.system.web.flash.AbstractFlashScope)

    • logbox: A reference to the application (coldbox.system.logging.LogBox)

    • log: A pre-configured logging (coldbox.system.logging.Logger)

    • wirebox : A reference to the application (coldbox.system.ioc.Injector)

    • $super: A reference to the virtual super class if using non-inheritance approach.

    ColdBox Modules
    COLDBOX-995arrow-up-right
    COLDBOX-994arrow-up-right
    COLDBOX-993arrow-up-right
    COLDBOX-992arrow-up-right
    COLDBOX-990arrow-up-right
    COLDBOX-987arrow-up-right
    COLDBOX-986arrow-up-right
    COLDBOX-985arrow-up-right
    COLDBOX-984arrow-up-right
    WIREBOX-95arrow-up-right
    WIREBOX-114arrow-up-right
    WIREBOX-113arrow-up-right

    My First ColdBox Application

    CommandBox comes with a coldbox create app command that can enable you to create application skeletons using one of our official skeletons or your own. Here are the names of the common ones you can find in our Github Organization:

    • AdvancedScript (default): A script based advanced template

    • elixir : A based template

    • ElixirBower : A + Bower based template

    • ElixirVueJS : A + Vue.js based template

    • rest: A RESTFul services template

    • rest-hmvc: A RESTFul service built with modules

    • Simple : A traditional simple template

    • SuperSimple : The bare-bones template

    circle-check

    You can find all our template skeletons here:

    hashtag
    Scaffolding Our Application

    So let's create our first app using the default template skeleton AdvancedScript:

    This will scaffold the application and also install ColdBox for you. The following folders/files are generated for you:

    Now let's start a server so we can see our application running:

    circle-info

    This will start up a 5 open source CFML engine. If you would like an Adobe ColdFusion server then just add to the command: cfengine=adobe@{version} where {version} can be: 2021,2018,2016.

    hashtag
    Default Event

    This command will start a server with URL rewrites enabled, open a web browser for you and execute the index.cfm which in turn executes the default event by convention in a ColdBox application: main.index. This is now our first convention!

    Instead of executing pages like in a traditional application, we always execute the same page but distinguish the event we want via . When no mappings are present we execute the default event by convention.

    circle-check

    Tip: ColdBox Events map to handlers (cfc) and appropriate actions (functions)

    circle-check

    Tip: The default event can be also changed in the configuration file: config/Coldbox.cfc

    Hooray, we have scaffolded our first application, started a server and executed the default event. Explore the application template generated, as it contains many useful information about your application.

    circle-check

    Tip: Type coldbox create app help to get help on all the options for creating ColdBox applications.

    hashtag
    File/Folder Conventions

    ColdBox is a conventions based framework, meaning you don't have to explicitly write everything. We have a few contracts in place that you must follow and boom, 🎉 things happen. The location and names of files and functions matter. Since we scaffolded our first application, let's write down in a table below with the different conventions that exist in ColdBox.

    circle-info

    What is the common denominator in all the conventions? That they are all optional.

    Nothing is really mandatory in ColdBox anymore.

    hashtag
    Re-initializing The Application

    There will be times when you make configuration or code changes that are not reflected immediately in the application due to caching. You can tell the framework to reinit or restart the application for you via the URL by leveraging the special URL variable fwreinit.

    You can also use CommandBox to reinit the application:

    circle-check

    Tip: You can add a password to the reinit procedures for further security, please see the .

    Resourceful Routes

    Resourceful routes are convention based to help you create routing with less boilerplate.

    In ColdBox, you can register resourceful routes (resources()) to provide automatic mappings between HTTP verbs and URLs to event handlers and actions by convention. By convention, all resources map to a handler with the same name or they can be customized if needed. This allows for a standardized convention when building routed applications and less typing 😉

    This single resource declaration will create all the necessary variations of URL patterns and HTTP Verbs to actions to handle the resource. Please see the table below with all the permutations it will create for you.

    Verb
    Route
    Event
    Purpose

    For in-depth usage of the resources() method, let's investigate the API Signature:

    hashtag
    Scaffolding Resources

    We have created a scaffolding command in CommandBox to help you register and generate resourceful routes. Just run the following command in CommandBox to get all the help you will need in generating resources:

    hashtag
    API Resourceful Routes

    If you are building mostly API routes and not full HTML app routes, you can use the shortcut method apiResources() method instead. This method will work the same as above BUT it will exclude the new and edit actions for you since we are in API Land.

    Verb
    Route
    Event
    Purpose

    Application.cfc
    component{
        // Application properties
        this.name = hash( getCurrentTemplatePath() );
        this.sessionManagement = true;
        this.sessionTimeout = createTimeSpan(0,0,30,0);
        this.setClientCookies = true;
    
        // COLDBOX STATIC PROPERTY, DO NOT CHANGE UNLESS THIS IS NOT THE ROOT OF YOUR COLDBOX APP
        COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() );
        // The web server mapping to this application. Used for remote purposes or static purposes
        COLDBOX_APP_MAPPING   = "";
        // COLDBOX PROPERTIES
        COLDBOX_CONFIG_FILE      = "";
        // COLDBOX APPLICATION KEY OVERRIDE
        COLDBOX_APP_KEY          = "";
        // By default if an app is reiniting and a request hits it, we will fail fast with a message
        COLDBOX_FAIL_FAST        = true;
    
        // application start
        public boolean function onApplicationStart(){
            application.cbBootstrap = new coldbox.system.Bootstrap( COLDBOX_CONFIG_FILE, COLDBOX_APP_ROOT_PATH, COLDBOX_APP_KEY, COLDBOX_APP_MAPPING, COLDBOX_FAIL_FAST );
            application.cbBootstrap.loadColdbox();
            return true;
        }
    
        // request start
        public boolean function onRequestStart(String targetPage){
            // Process ColdBox Request
            application.cbBootstrap.onRequestStart( arguments.targetPage );
    
            return true;
        }
    
        public void function onSessionStart(){
            application.cbBootStrap.onSessionStart();
        }
    
        public void function onSessionEnd( struct sessionScope, struct appScope ){
            arguments.appScope.cbBootStrap.onSessionEnd( argumentCollection=arguments );
        }
    
        public boolean function onMissingTemplate( template ){
            return application.cbBootstrap.onMissingTemplate( argumentCollection=arguments );
        }
    
    }
    Application.cfc
    component extends="coldbox.system.Bootstrap"{
    
        // Application properties
        this.name = hash( getCurrentTemplatePath() );
        this.sessionManagement = true;
        this.sessionTimeout = createTimeSpan(0,0,30,0);
        this.setClientCookies = true;
    
        // COLDBOX STATIC PROPERTY, DO NOT CHANGE UNLESS THIS IS NOT THE ROOT OF YOUR COLDBOX APP
        COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() );
        // The web server mapping to this application. Used for remote purposes or static purposes
        COLDBOX_APP_MAPPING   = "";
        // COLDBOX PROPERTIES
        COLDBOX_CONFIG_FILE      = "";
        // COLDBOX APPLICATION KEY OVERRIDE
        COLDBOX_APP_KEY          = "";
        // By default if an app is reiniting and a request hits it, we will fail fast with a message
        COLDBOX_FAIL_FAST        = true;
    }
    application.bootstrapper.setLockTimeout( 10 );
     coldbox.eventCaching = true;
    // In Script
    function showEntry(event,rc,prc) cache="true" cacheTimeout="30" cacheLastAccessTimeout="15"{
        //get Entry
        prc.entry = getEntryService().getEntry(event.getValue('entryID',0));
    
        //set view
        event.setView('blog/showEntry');
    }
    templateCache = cachebox.getCache( "template" );
    //Trigger to purge all Events
    getCache( "template" ).clearAllEvents();
    
    //Trigger to purge all events synchronously
    getCache( "template" ).clearAllEvents(async=false);
    
    //Purge all events from the blog handler
    getCache( "template" ).clearEvent('blog');
    
    //Purge all permutations of the blog.dspBlog event
    getCache( "template" ).clearEvent('blog.dspBlog');
    
    //Purge the blog.dspBlog event with entry of 12345
    getCache( "template" ).clearEvent('blog.dspBlog','id=12345')
    this.EVENT_CACHE_SUFFIX = function( eventHandlerBean ){
      return "a localized string, etc";
    };
    component{
    
        onRequestCapture(event,interceptData){
            var rc = event.getCollection();
    
            // Add user's locale to the request collection to influence event caching
            rc._user_locale = getFWLocale();
        }
    
    }
    box install cbdebugger
    coldbox.handlersExternalLocation  = "shared.myapp.handlers";
    // Rescan handlers on each request, turn OFF in production please
    coldbox.handlersIndexAutoReload = true;
    // Deactivate singleton caching of the handlers, turn ON in production pleaese
    coldbox.handlerCaching = false;
    Main.cfc
    component extends="coldbox.system.EventHandler"{
    
        /**
         * Default Action
         */
        function index( event, rc, prc ){
            prc.message = "Hello From ColdBox";
            event.setView( "main/index");
        }
    
        /**
         * Action returning complex data, converted to JSON automatically by ColdBox
         */
        function data( event, rc, prc ){
            var data = getInstance( "MyModel" ).getArray();
            return data; 
        }
    
    }
    function name( event, rc, prc )
    component extends="coldbox.system.EventHandler"{
    
        function index( event, rc, prc ){
            return "<h1> Hi from handler land!</h1>";
        }
    
        function save( event, rc, prc ){
            getInstance( "MyService" ).save( rc );
            relocate( "users/list" );
        }
    
        function myData( event, rc, prc ){
            return ['coldbox', 'wirebox', 'cachebox', 'logbox'];
        }
    }
    // Creates all resources that point to a photos event handler by convention
    resources( "photos" );
    
    // Register multiple fluently
    resources( "photos" )
        .resources( "users" )
        .resources( "contacts" );
    
    // Creates all resources to the event handler of choice instead of convention
    resources( resource="photos", handler="MyPhotoHandler" );
    
    // All resources in a module
    resources( resource="photos", handler="photos", module="api" );
    
    // Resources in a ModuleConfig.cfc
    router.resources( "photos" )
      .resources( resource="users", handler="user" )

    GET

    /photos/:id/edit

    photos.edit

    Return the HTML form for editing the photo

    PUT/PATCH

    /photos/:id

    photos.update

    Update a photo by id

    DELETE

    /photos/:id

    photos.delete

    Delete a photo by id

    photos.show

    Show a photo by id

    PUT/PATCH

    /photos/:id

    photos.update

    Update a photo by id

    DELETE

    /photos/:id

    photos.delete

    Delete a photo by id

    GET

    /photos

    photos.index

    Get all photos

    GET

    /photos/new

    photos.new

    Return the HTML form for creating a photo

    POST

    /photos

    photos.create

    Create a photo

    GET

    /photos/:id

    photos.show

    GET

    /photos

    photos.index

    Get all photos

    POST

    /photos

    photos.create

    Create a photo

    GET

    Show a photo by id

    /photos/:id

    /**
     * Create all RESTful routes for a resource. It will provide automagic mappings between HTTP verbs and URLs to event handlers and actions.
     *
     * By convention the following rules apply
     * - The name of the resource maps to the name of the event handler
     * - The default paremeter name is called `:id`
     * - The available actions are: index, new, create, show, edit, update, delete
     *
     * Example: `resource = photos` Then we will create the following routes:
     * - `/photos` : `GET` -> `photos.index` Display a list of photos
     * - `/photos/new` : `GET` -> `photos.new` Returns an HTML form for creating a new photo
     * - `/photos` : `POST` -> `photos.create` Create a new photo
     * - `/photos/:id` : `GET` -> `photos.show` Display a specific photo
     * - `/photos/:id/edit` : `GET` -> `photos.edit` Return an HTML form for editing a photo
     * - `/photos/:id` : `PUT/PATCH` -> `photos.update` Update a specific photo
     * - `/photos/:id` : `DELETE` -> `photos.delete` Delete a specific photo
     *
     * @resource      The name of a single resource to map
     * @handler       The handler for the route. Defaults to the resource name.
     * @parameterName The name of the id/parameter for the resource. Defaults to `id`.
     * @only          Limit routes created with only this list or array of actions, e.g. "index,show"
     * @except        Exclude routes with an except list or array of actions, e.g. "show"
     * @module        If passed, the module these resources will be attached to.
     * @namespace     If passed, the namespace these resources will be attached to.
     * @pattern       If passed, the actual URL pattern to use, else it defaults to `/#arguments.resource#` the name of the resource.
     * @meta          A struct of metadata to store with ALL the routes created from this resource
     */
    function resources(
    	required resource,
    	handler,
    	parameterName    = "id",
    	only             = [],
    	except           = [],
    	string module    = "",
    	string namespace = "",
    	string pattern   = "",
    	struct meta      = {}
    ){
    coldbox create resource help
    apiResources( "users" );
    apiResources( "photos" );

    Layouts

    models

    false

    Model layer business objects

    modules

    false

    CommandBox Tracked Modules

    modules_app

    false

    Custom Modules You Write

    tests

    false

    A test harness with runners, specs and more.

    views

    false

    Views

    File/Folder Convention

    Mandatory

    Description

    config/Coldbox.cfc

    false

    The application configuration file

    config/Router.cfc

    false

    The application URL router

    handlers

    false

    Event Handlers (controllers)

    layouts

    ColdBox Elixirarrow-up-right
    ColdBox Elixirarrow-up-right
    ColdBox Elixirarrow-up-right
    github.com/coldbox-templatesarrow-up-right
    Luceearrow-up-right
    URL routing
    configuration sectionarrow-up-right

    false

    LogBox arrow-up-right
    logger objectarrow-up-right
    WireBox Injector arrow-up-right

    Routing Methods

    Apart from routing by convention, you can also register your own expressive routes. Let's investigate the routing approaches.

    hashtag
    Inline Terminator

    The route() method allows you to register a pattern and immediately assign it to execute an event or a response via the target argument.

    The first pattern registers and if matched it will execute the wiki.page event. The second pattern if matched it will execute the profile.show event from the users module and register the route with the userprofile name.

    hashtag
    Inline Responses

    You can also pass in a closure or lambda to the target argument and it will be treated as an inline action:

    You can also pass just an HTML string with {rc_var} replacements for the routed variables place in the request collection

    hashtag
    Routing to Events

    If you will not use the inline terminators you can do a full expressive route definition to events using the to() method, which allows you to concatenate the route pattern with modifiers:

    hashtag
    Routing To Handler Action Combinations

    You can also route to a handler and an action using the modifiers instead of the to() method. This long-form is usually done for visibility or dynamic writing of routes. You can use the following methods:

    • withHandler()

    • withAction()

    • toHandler()

    hashtag
    Routing to Views

    You can also route to views and view/layout combinations by using the toView() terminator:

    hashtag
    Routing to Redirects

    You can also use the toRedirect() method to re-route patterns to other patterns.

    circle-info

    The default status code for redirects are 301 redirects which are PERMANENT redirects.

    hashtag
    Dynamic Routing Redirection

    You can also pass a closure as the target of relocation. This closure will received the parsed parameters, the incoming route record and the event object. You can determine dynamically where the relocation will go.

    This is great if you need to actually parse the incoming route and do a dynamic relocation.

    Happy Redirecting!

    hashtag
    Routing to Handlers

    You can also redirect a pattern to a handler using the toHandler() method. This is usually done if you have the action coming in via the URL or you are using RESTFul actions.

    hashtag
    Routing to RESTFul Actions

    You can also route a pattern to HTTP RESTFul actions. This means that you can split the routing pattern according to incoming HTTP Verb. You will use a modifier withAction() and then assign it to a handler via the toHandler() method.

    hashtag
    Routing to Responses

    The Router allows you to create inline responses via closures/lambdas or enhanced strings to incoming URL patterns. You do not need to create handler/actions, you can put the actions inline as responses.

    If you use a response closure/lambda, they each accepts three arguments:

    1. event - An object that models and is used to work with the current request (Request Context)

    2. rc - A struct that contains both URL/FORM variables merged together (unsafe data)

    If the response is an HTML string, then you can do {rc_var} replacements on the strings as well:

    hashtag
    Sub-Domain Routing

    You can also register routes that will respond to sub-domains and even capture portions of the sub-domain for multi-tenant applications or SaaS applications. You will do this using the withDomain() method.

    You can leverage the full routing DSL as long as you add the withDomain() call with the domain you want to bind the route to. Also note that the domain string can contain placeholders which will be translated to RC variables for you if matched.

    circle-check

    Tip: Please note that you can leverage as well for domains

    hashtag
    Adding Variables to RC/PRC

    You can also add variables to the RC and PRC structs on a per-route basis by leveraging the following methods:

    • rc( name, value, overwrite=true ) - Add an RC value if the route matched

    • rcAppend map, overwrite=true ) - Add multiple values to the RC collection if the route matched

    This is a great way to manually set variables in the incoming structures:

    hashtag
    Routing Conditions

    You can also apply runtime conditions to a route in order for it to be matched. This means that if the route matches the URL pattern then we will execute a closure/lambda to make sure that it meets the runtime conditions. We will do this with the withCondition() method.

    Let's say you only want to fire some routes if they are using Firefox, or a user is logged in, or whatever.

    Rendering Data

    Handler actions can return data back to its callers in many different formats. Either to create RESTFul services, or just send data that's not HTML back to the user. The different usages can be:

    • Complex Data

    • HTML

    coldbox create app Quickstart
    + coldbox // The ColdBox framework library (CommandBox Tracked)
    + config // Configuration files
    + handlers // Your handlers/controllers
    + includes // static assets
    + interceptors // global interceptors
    + layouts // Your layouts
    + lib // Java Jars to load
    + models // Your Models
    + modules // CommandBox Tracked Modules
    + modules_app // Custom modules
    + tests // Test harness
    + views // Your Views
    + Application.cfc // Bootstrap
    + box.json // CommandBox package descriptor
    + index.cfm // Front controller
    server start
    http://localhost:{port}/?fwreinit=1
    coldbox reinit
    route( "/wiki:pagename", "wiki.page" );
    route( 
        pattern="/users/:id/profile", 
        target="users:profile.show", 
        name="userprofile" 
    );
  • end()

  • prc - A secondary struct that is private only settable from within your application (safe data)

    prc( name, value, overwrite=true ) - Add an PRC value if the route matched

  • prcAppend map, overwrite=true ) - Add multiple values to the PRC collection if the route matched

  • Routing Groups
    Rendered Data via event.renderData()
  • Uniform Response Data

  • circle-check

    The last option on the list is to support native REST Applications in ColdBox by leveraging the REST Handler.

    hashtag
    Complex Data

    By default, any complex data returned from handler actions will automatically be marshaled to JSON:

    Simple as that. ColdBox detects the complex object and tries to convert it to JSON for you automatically.

    hashtag
    renderdata Action Annotation

    If you want ColdBox to marshall the content to another type like XML or PDF. Then you can use the renderdata annotation on the action itself. The renderdata annotation can be any of the following values:

    • json

    • jsonp

    • jsont

    • xml

    • html

    • text

    • pdf

    hashtag
    renderdata Component Annotation

    You can also add the renderData annotation to the component definition and this will override the default of JSON. So if you want XML as the default, you can do this:

    hashtag
    $renderData Convention

    If the returned complex data is an object and it contains a function called $renderData(), then ColdBox will call it for you automatically. So instead of marshaling to JSON automatically, your object decides how to marshal itself.

    hashtag
    Native HTML

    By default if your handlers return simple values, then they will be treated as returning HTML.

    hashtag
    event.renderData()

    Using the renderdata() method of the event object is the most flexible for RESTFul web services or pure data marshaling. Out of the box ColdBox can marshall data (structs, queries, arrays, complex or even ORM entities) into the following output formats:

    • XML

    • JSON

    • JSONP

    • JSONT

    • HTML

    • TEXT

    • PDF

    • WDDX

    • CUSTOM

    Here is the method signature:

    Below are a few simple examples:

    As you can see, it is very easy to render data back to the browser or caller. You can even choose plain and send HTML back if you wanted to.

    hashtag
    Render PDFs

    You can also render out PDFs from ColdBox using the render data method. The data argument can be either the full binary of the PDF or simple values to be rendered out as a PDF; like views, layouts, strings, etc.

    There is also a pdfArgs argument in the render data method that can take in a structure of name-value pairs that will be used in the cfdocument (See docsarrow-up-right) tag when generating the PDF. This is a great way to pass in arguments to really control the way PDF's are generated uniformly.

    hashtag
    Renderdata With Formats

    The renderData() method also has two powerful arguments: formats & formatsView. If you currently have code like this:

    Where you need to param the incoming format extension, then do a switch and do some code for marshalling data into several formats. Well, no more, you can use our formats argument and ColdBox will marshall and code all that nasty stuff for you:

    That's it! ColdBox will figure out how to deal with all the passed in formats for you that renderdata can use. By convention it will use the name of the incoming event as the view that will be rendered for HTML and PDF; implicit views. If the event was users.list, then the view would be views/users/list.cfm. However, you can tell us which view you like if it is named different:

    If you need to redirect for html events, you can pass any arguments you normally would pass to setNextEvent to formatsRedirect.

    hashtag
    Custom Data Conversion

    You can do custom data conversion by convention when marshalling CFCs. If you pass in a CFC as the data argument and that CFC has a method called $renderdata(), then the marshalling utility will call that function for you instead of using the internal marshalling utilities. You can pass in the custom content type for encoding as well:

    The CFC converter:

    In this approach your $renderdata() function can be much more customizable than our internal serializers. Just remember to use the right contentType argument so the browser knows what to do with it.

    route(
        pattern="/echo",
        target=function( event, rc, prc ){
            return "hello ColdBox!";
        }
    );
    
    route(
        pattern="/users",
        target=function( event, rc, prc ){
            return getInstance( "UserService" ).list();
        }
    );
    route(
        "/echo/:name",
        "<h1>Hello {name} how are you today!</h1>"
    );
    route( "/wiki/:pagename" )
        .to( "wiki.show" );
    
    route( "/users/:id/profile" )
        .as( "userProfile" )
        .to( "users:profile.show" );
    
    route( "/users/:id/profile" )
        .as( "userProfile" )
        .withVerbs( "GET" )
        .withSSL()
        .header( "cache", false )
        .prc( "isActive", true )
        .to( "users:profile.show" );
    route( "wiki/:pagename" )
        .as( "wikipage" )
        .withAction( "show" )
        .toHandler( "wiki" );
    
    route( "wiki/:pagename" )
        .withHander( "wiki" )
        .withAction( "show" )
        .end();
    route( "/contact-us" )
        .toView( 
            view = "view name",
            layout = "layout",
            nolayout = false,
            viewModule = "moduleName",
            layoutModule = "moduleName"
        );
    route( "/my-old/link" )
        .toRedirect( target="/new/pattern", statusCode=301 );
    route( "/my-old/link" )
        .toRedirect( ( route, params, event ) => "/new/route" )
      
    route( "/my-old/link" )
        .toRedirect( ​function( route, params, event ){ 
            return "/new/route"; 
    } ) 
    route( "/old/api/users/:id" )    
        .toRedirect( ( route, params, event ) => { 
            return "/api/v1/users/#params.id#" } 
        )
    // Action comes via the URL
    route( "/users/:action" )
        .toHandler( "users" );
    // RESTFul actions
    route( "/users/:id?" )
        .withAction( {
            GET : "index",
            POST : "save",
            PUT : "update",
            DELETE : "remove"
        } )
        .toHandler( "users" );
    /**
     * Setup a response for a URL pattern
     * @body A closure/lambda or enhanced HTML string
     * @statusCode The status code to send
     * @statusText The status code to send
     */
    function toResponse( 
        required body, 
        numeric statusCode = 200, 
        statusText = "Ok" 
    )
    // Simple response routing
    route( "/users/hello", function( event, rc, prc ){
        return "<h1>Hello From RESTLand</h1>";
    } );
    
    // Simple response routing with placeholders
    route( "/users/:username", function( event, rc, prc ){
        return "<h1>Hello #encodeForHTML( rc.username )# From RESTLand</h1>";
    } );
    
    // Routing with the toResponse() method
    route( "/users/:id" )
        .toResponse( function( event, rc, prc ){
            var oUser = getInstance( "UserService" ).get( rc.id ?: 0 );
            if( oUser.isLoaded() ){
                return oUser.getMemento();
            }
            event.setHTTPHeader( statusCode = 400, statusText = "Invalid User ID provided" );
            return {
                "error" : true,
                "messages" : "Invalid User ID Provided"
            };
        } );
    // Routing with enhanced HTML strings
    route( "/users/:id" )
        .toResponse(
            "<h1>Welcome back user: {id} how are you today!</h1>"
        );
    route( "/" )
      .withDomain( "subdomain-routing.dev" )
      .to( "subdomain.index" );
    
    route( "/" )
      .withDomain( ":username.forgebox.dev" )
      .to( "subdomain.show" );
    route( "/api/v1/users/:id" )
        .rcAppend( { secured : true } )
        .prcAppend( { name : "hello" } )
        .to( "api-v1:users.show" );
    route( "/go/firefox" )
      withCondition( function( requestString ){
        return ( findnocase( "Firefox", cgi.HTTP_USER_AGENT ) ? true : false );
      });
      .to( "firefox.index" );
    function showData( event, rc, prc ){
        prc.data = service.getUsers();
        return prc.data;
    }
    function usersAsXML( event, rc, prc ) renderdata='xml'{
        prc.data = service.getUsers();
        return prc.data;
    }
    
    function usersAsPDF( event, rc, prc ) renderdata='pdf'{
        prc.data = service.getUsers();
        return prc.data;
    }
    component renderdata="xml"{
    
    }
    function showUser( event, rc, prc ){
        return service.getUser( 2 );
    }
    function index(event,rc,prc){
        return "<h1>Hello from my handler today at :#now()#</h1>";
    }
    
    function myData( event, rc, prc ){
         prc.mydata = myservice.getData();
         return renderView( "main/myData" );
    }
    /**
    * Use this method to tell the framework to render data for you. The framework will take care of marshalling the data for you
    * @type The type of data to render. Valid types are JSON, JSONP, JSONT, XML, WDDX, PLAIN/HTML, TEXT, PDF. The deafult is HTML or PLAIN. If an invalid type is sent in, this method will throw an error
    * @data The data you would like to marshall and return by the framework
    * @contentType The content type of the data. This will be used in the cfcontent tag: text/html, text/plain, text/xml, text/json, etc. The default value is text/html. However, if you choose JSON this method will choose application/json, if you choose WDDX or XML this method will choose text/xml for you.
    * @encoding The default character encoding to use.  The default encoding is utf-8
    * @statusCode The HTTP status code to send to the browser. Defaults to 200
    * @statusText Explains the HTTP status code sent to the browser.
    * @location Optional argument used to set the HTTP Location header
    * @jsonCallback Only needed when using JSONP, this is the callback to add to the JSON packet
    * @jsonQueryFormat JSON Only: This parameter can be a Boolean value that specifies how to serialize ColdFusion queries or a string with possible values "row", "column", or "struct".
    * @jsonAsText If set to false, defaults content mime-type to application/json, else will change encoding to plain/text
    * @xmlColumnList XML Only: Choose which columns to inspect, by default it uses all the columns in the query, if using a query
    * @xmlUseCDATA XML Only: Use CDATA content for ALL values. The default is false
    * @xmlListDelimiter XML Only: The delimiter in the list. Comma by default
    * @xmlRootName XML Only: The name of the initial root element of the XML packet
    * @pdfArgs All the PDF arguments to pass along to the CFDocument tag.
    * @formats The formats list or array that ColdBox should respond to using the passed in data argument. You can pass any of the valid types (JSON,JSONP,JSONT,XML,WDDX,PLAIN,HTML,TEXT,PDF). For PDF and HTML we will try to render the view by convention based on the incoming event
    * @formatsView The view that should be used for rendering HTML/PLAIN/PDF. By default ColdBox uses the name of the event as an implicit view
    * @formatsRedirect The arguments that should be passed to relocate as part of a redirect for the HTML action.  If the format is HTML and this struct is not empty, ColdBox will call relocate with these arguments.
    * @isBinary Bit that determines if the data being set for rendering is binary or not.
    */
    function renderData(
        type="HTML",
        required data,
        contentType="",
        encoding="utf-8",
        numeric statusCode=200,
        statusText="",
        location="",
        jsonCallback="",
         jsonQueryFormat="true",
        boolean jsonAsText=false,
        xmlColumnList="",
        boolean xmlUseCDATA=false,
        xmlListDelimiter=",",
        xmlRootName="",
        struct pdfArgs={},
        formats="",
        formatsView="",
        formatsRedirect={},
        boolean isBinary=false
    ){
    // html marshalling
    function renderHTML(event,rc,prc){
        event.renderData( data="<h1>My HTML</h1>" );
    }
    // xml marshalling
    function getUsersXML(event,rc,prc){
        var qUsers = getUserService().getUsers();
        event.renderData( type="XML", data=qUsers );
    }
    //json marshalling
    function getUsersJSON(event,rc,prc){
        var qUsers = getUserService().getUsers();
        event.renderData( type="json", data=qUsers, statusCode=403 );
    }
    // from binary
    function pdf(event,rc,prc){
      var binary = fileReadAsBinary( file.path );
      event.renderData( data=binary, type="PDF" );
    }
    
    // from content
    function pdf(event,rc,prc){
      event.renderData( data=renderView("views/page"), type="PDF" );
    }
    // from content and with pdfArgs
    function pdf(event,rc,prc){
      var pdfArgs = { bookmark = "yes", backgroundVisible = "yes", orientation="landscape" };
      event.renderData(data=renderView("views/page"), type="PDF", pdfArgs=pdfArgs);
    }
    event.paramValue("format", "html");
    
    switch( rc.format ){
        case "json" : case "jsonp" : case "xml" : {
            event.renderData(data=mydata, type=rc.format);
            break;
        } 
        case "pdf" : {
            event.renderData(data=renderView("even/action"), type="pdf");
            break;
        }
        case "html" : {
            event.setView( "event/action" );
            break;
          }
    };
    event.renderData( data=MyData, formats="xml,json,html,pdf" );
    event.renderData( data=MyData, formats="xml,json,html,pdf", formatsView="data/MyView" );
    event.renderData( data=MyData, formats="xml,json,html,pdf", formatsRedirect={event="Main.index"} );
    // get an instance of your custom converter
    myConverter = getInstance("MyConverter")
    // put some data in it
    myConverter.setData( data );
    // marshall it out according to your conversions and the content type it supports
    event.renderData( data= myConverter, contentType=myConverter.getContentType() );
    component accessors="true"{
    
        property name="data" type="mytype";
        property name="contentType";
    
        function init(){ 
            setContentType("text");
            return this; 
        }
    
        // The magical rendering
        function $renderdata(){
            var d = {
                n = data.getName(),
                a = data.getAge(),
                c = data.getCoo(),
                today = now()
            };
    
            return d.toString();
        }
    
    }

    Getting Started Guide

    The ColdBox HMVC Platform is the de-facto enterprise-level HMVC framework for CFML developers.

    The ColdBox HMVC Platform is the de-facto enterprise-level HMVC framework for CFML developers. It's professionally backed, highly extensible, and productive. Getting started with ColdBox is quick and painless. The only thing you need to begin is , a command line tool for CFML developers.

    This is a one-page introductory guide to ColdBox. If you are new to MVC or ColdBox, you can also leverage our as well.

    hashtag
    Need Help

    The Ortus Community is the way to get any type of help for our entire platform and modules: https://community.ortussolutions.comarrow-up-right

    hashtag
    IDE Tools

    ColdBox has the following supported IDE Tools:

    • Sublime - https://packagecontrol.io/packages/ColdBox Platformarrow-up-right

    • VSCode - https://marketplace.visualstudio.com/items?itemName=ortus-solutions.vscode-coldboxarrow-up-right

    • CFBuilder - https://www.forgebox.io/view/ColdBox-Platform-Utilitiesarrow-up-right

    hashtag
    Install CommandBox

    You can read through our one-page CommandBox Getting Started Guidearrow-up-right. Or simply grab the CommandBox executable from the download pagearrow-up-right and double click it to run.

    http://www.ortussolutions.com/products/commandboxarrow-up-right

    You should now be seeing a prompt that looks like this:

    CommandBox Shell

    hashtag
    Create A New Site

    Now we're cooking with gas! Let's create a new ColdBox application. CommandBox comes with built-in commands for scaffolding out new sites as well as installing ColdBox and other libraries. We'll start by changing into an empty directory were we want our new app to live. If necessary, you can create a new folder.

    Now let's ask CommandBox to create a new ColdBox app for us.

    circle-check

    Tip: You can find many scaffolding templates for ColdBox in our Github organization: github.com/coldbox-templatesarrow-up-right

    You can also issue a coldbox create app help command and get help for the creation command.

    hashtag
    File/Folder Conventions

    This command will place several new folders and files in your working directory. Let's run the ls command to view them.

    Here's a rundown of the important bits (Even thought they might be more generated files/folders)

    • coldbox - This is the ColdBox framework managed by CommandBox

    • config/Coldbox.cfc - Your application configuration object

    • config/Router.cfc - Your application URL Router

    • handlers - Your controller layer, which in ColdBox they are called event handlers

    • layouts - Your HTML layouts

    • models - This holds your model CFCs

    • modules - This holds the CommandBox tracked modules

    • modules_app - This holds your app's modules

    • tests - Your test harness for unit and integration testing

    • views - Your HTML views will go here

    hashtag
    Start It Up

    Now that our shiny new MVC app is ready to go, let's fire it up using the embedded server built into CommandBox. You don't need any other software installed on your PC for this to work. CommandBox has it all!

    In a few seconds, a browser window will appear with your running application. This is a full server with access to the web administrator where you can add data sources, mappings, or adjust the server settings. Notice the handy icon added to your system tray as well. The --rewritesEnable flag will turn on some basic URL rewriting so we have nice, pretty URLs.

    circle-check

    Tip: If you are creating an app to run on any other server than the commandbox server, you will need to manually set up URL rewriting. More info here: /the-basics/routing/requirements

    Generated ColdBox App

    hashtag
    Take A Look Around

    ColdBox uses easy conventions to define the controllers and views in your app. Let's open up our main app controller in your default editor to have a looksie.

    At the top, you'll see a function named "index". This represents the default action that runs for this controller, which in ColdBox land they are referred to as event handlers.

    Now let's take a look in the main/index view. It's located int he views folder.

    This line of code near the top of the view is what outputs the prc.welcomeMessage variable we set in the controller.

    Try changing the value being set in the handler and refresh your browser to see the change.

    hashtag
    Building On

    Let's define a new event handler now. Your controllers act as event handlers to respond to requests, REST API, or remote proxies.

    Pull up CommandBox again and run this command.

    That's it! You don't need to add any special configuration to declare your handler. Now we have a new handler called helloWorld with actions index, add, edit, and list. The command also created a test case for our handler as well as stubbed-out views for each of the actions.

    Now, let's re-initialize the framework to pick up our new handler by typing ?fwreinit=1 at the end of the URL.

    Let's hit this new controller we created with a URL like so. Your port number will probably be different.

    127.0.0.1:43272/helloWorld

    circle-info

    Normally the URL would have index.cfm before the /helloWorld bit, but our --rewritesEnable flag when we started the server makes this nicer URL possible.

    hashtag
    Install Packages

    ColdBox's MVC is simple, but its true power comes from the wide selection of modules you can install into your app to get additional functionality. You can checkout the full list of modules available on the FORGEBOX directory: www.forgebox.ioarrow-up-right.

    forgebox.io/type/modulesarrow-up-right

    Here's some useful examples:

    • BCrypt -- Industry-standard password hashing

    • cbdebugger -- For debugging Coldbox apps

    • cbjavaloader - For interacting with Java classes and libraries

    • cbMarkdown - For writing in markdown

    • cbMessagebox -- Display nice error/success messages

    • cborm -- Awesome ORM Services

    • cb18n -- For multilingual sites

    • cbt - ColdBox templating language

    • cbValidation - Back-end validation framework

    • qb - Fluent query builder and schema builder

    • route-visualizer - For visualizing your application routes

    Install cbmessagebox from the CommandBox prompt like this:

    We can see the full list of packages by using the list command.

    Right now we can see that our app depends on coldbox and cbmessagebox to run. We'll use our new cbmessagebox module in a few minutes. But first, we'll create a simple Model CFC to round out our MVC app.

    hashtag
    Creating A Model

    Models encapsulate the business logic your application. They can be services, beans, or DAOs. We'll use CommandBox to create a GreeterService in our new app with a sayHello method.

    circle-check

    Tip: The --open is a nice shortcut that opens our new model in our default editor after creating it.

    Let's finish implementing the sayHello() method by adding this return statement and save the file.

    We can also add the word singleton to the component declaration. This will tell WireBox to only create one instance of our service.

    circle-info

    What is WireBox?

    WireBox is a dependency injection framework that is included with ColdBox. It will manage all object creations, persistence and assembling. You don't have to worry about using new or createobject() for CFCs anymore.

    hashtag
    Tie It All Together

    Ok, let's open up that helloWorld handler we created a while back. Remember, you can hit tab while typing to auto-complete your file names.

    We'll inject our greeterService and the cbmessagebox service into the handler by adding these properties to the top of /handlers/helloWorld.cfc.

    circle-info

    What is this magical injection? Injection is a way to get references of other objects placed in the variables scope of other objects. This makes your life easier as you don't have to be creating objects manually or even knowing where they exist.

    This will put the instance of our services in the variables scope where we can access it in our action methods.

    And now in our index method, we'll set the output of our service into an info message.

    One final piece. Open up the default layout located in layouts/Main.cfm and find the #renderView()#. Add this line right before it to render out the message box that we set in our handler.

    Now hit your helloWorld handler one final time with ?fwreinit=1 in the URL to see it all in action! (Again, your port number will most likely be different.

    127.0.0.1:43272/helloWorld?fwreinit=1

    hashtag
    What's Next?

    Congratulations! In a matter of minutes, you have created a full MVC application. You installed a community module from ForgeBox, created a new handler/view and tied in business logic from a service model.

    As easy as that was, you're just scratching the surface of what ColdBox can do for you. Continue reading this book to learn more about:

    • Environment-specific configuration

    • Easy SES URL routing

    • Tons of 3rd party modules

    • Drop-in security system

    • Sweet REST web service support

    hashtag
    Getting Help

    If you run into issues or just have questions, please jump on our Ortus Communityarrow-up-right and our Slack teamarrow-up-right and ask away.

    ColdBox is Professional Open Source under the Apache 2.0 license. We'd love to have your help with the product.

    CommandBoxarrow-up-right
    60 minute quick start guide
    Introduction | ColdBox Elixircoldbox-elixir.ortusbooks.comchevron-right
    ColdBox Elixir Docs
    CommandBox> mkdir playground --cd
    CommandBox> coldbox create app MyPlayground
    CommandBox> ls
    CommandBox> start --rewritesEnable
    CommandBox> edit handlers/main.cfc
    // Default Action
    function index(event,rc,prc){
        prc.welcomeMessage = "Welcome to ColdBox!";
        event.setView( "main/index" );
    }
    CommandBox> edit views/main/index.cfm
    <h1>#prc.welcomeMessage#</h1>
    prc.welcomeMessage = "This is my new welcome message";
    CommandBox> coldbox create handler helloWorld index,add,edit,list
    CommandBox> install cbmessagebox
    CommandBox> list
    Dependency Hierarchy for myApp (0.0.0)
    +-- cbmessagebox (1.0.0)
    +-- coldbox (4.0.0)
    CommandBox> coldbox create model GreeterService sayHello --open
    component singleton {
    
        function sayHello(){
            return 'Hey you, have an awesome day!';
        }
    
    }
    CommandBox> edit handlers/helloWorld.cfc
    component {
    
        property name='greeterService' inject='greeterService';
        property name='messageBox' inject='@cbmessagebox';
    
        ...
    }
    function index( event, rc, prc ){
        messageBox.info( greeterService.sayHello() );
        event.setView( "helloWorld/index" );
    }
    #getInstance( 'messagebox@cbmessageBox').renderIt()#
    <div class="container">#renderView()#</div>
    Logo

    What's New With 6.6.0

    February 02, 2022

    hashtag
    Major Updates

    hashtag
    API Resourceful Routes

    We have created a shortcut approach to creating RESTFul API resources in your ColdBox Routers via the new method apiResources(). This method will create all the routes for your API service with no HTML views.

    Verb
    Route
    Event
    Purpose

    hashtag
    Module Enhancements

    We have made several enhancements with modules:

    • Performance optimizations when registering and activating modules

    • Modules now track their own registration and activation load times (Which are now visible in )

    • Better logging of modules when they activate and register

    hashtag
    Experimental New App Structure

    We have been working on a new directory structure for ColdBox applications where the web root is not just dumped with everything on it. We have made several internal tickets to allow for this to work and we have a very early alpha available in github:

    hashtag
    Scheduler and Task Updates

    There are so many tickets that helped resolved issues with the ColdBox schedulers and scheduled tasks. We have also solidifying our Adobe scope issues and brought many new helpers for developers when building task objects.

    hashtag
    Integration Testing of Subdomain/Domain Routing

    If you are building multi-tenant applications with ColdBox and are leveraging , then you can easily use the domain argument in all of our request(), execute() and HTTP Verb methods to simulate the domain in play for THAT specific spec execution.

    hashtag
    Custom Session Identifiers

    ColdBox has always had its internal way of figuring out what identifier to use for each user's request based on the way ColdFusion works. However, now you can influence and provide your own approach instead of relying on the core CFML approach. You will do this by adding a coldbox.identifierProvider closure/lambda into your config/Coldbox.cfc.

    hashtag
    WireBox Child Injectors

    Welcome to the world of hierarchical dependency injection. We had the ability before to add a parent injector to WireBox, but now you can not only add a parent, but also many children to the hierarchy.

    Every injector has the capability to store an ordered collection (ordered struct) of child injectors via the childInjectors property. Child injectors are used internally in many instances to provide a hierarchical approach to DI where instances can be searched for locally, in the parent and in the children. Here are some of the new methods to assist with child injectors:

    • hasChildInjector( name ) - Verify if a child injector has been registered

    • registerChildInjector( name, child ) - Register a child injector by name

    • removeChildInjector( name )

    hashtag
    Child Enhanced Methods

    • getInstance()

      • The getInstance()method now has an injector argument so you can EXPLICITLY request an instance from a child injector by name getInstance( name : "service", injector : "childInjector" )

    hashtag
    Getting Instances From Specific Child Injectors

    The getInstance() has been modified to have an injector argument that you can use to specifically ask for an instance from that child injector. If the child injector has not been registered you will get a InvalidChildInjector Exception.

    hashtag
    Child Injector Explicit DSL

    The following is the DSL you can use to explicitly target a child injector for a dependency. You will prefix it with wirebox:child:{name} and the name of the injector:

    hashtag
    IInjector Interface Updates

    The coldbox.system.ioc.IInjector interface's getInstance() method has been modified to include support for child injector retrievals:

    hashtag
    Release Notes

    Bug

    • Non config apps fails since the core Settings.cfc had the configure() method removed

    • Framework Initialization Fails in @be on AutoWire of App Scheduler

    Model Integration

    We have a complete section dedicated to the Model Layer, but we wanted to review a little here since event handlers need to talk to the model layer all the time. By default, you can interact with your models from your event handlers in two ways:

    • Dependency Injection (Aggregationarrow-up-right)

    • Request, use and discard model objects (Associationarrow-up-right)

    ColdBox offers its own dependency injection framework, , which allows you, by convention, to talk to your model objects. However, ColdBox also allows you to connect to third-party dependency injection frameworks via the IOC module:

    circle-info

    Aggregation differs from ordinary composition in that it does not imply ownership. In composition, when the owning object is destroyed, so are the contained objects. - wikipedia

    hashtag
    Dependency Injection

    Your event handlers can be autowired with dependencies from by convention. By autowiring dependencies into event handlers, they will become part of the life span of the event handlers (singletons), since their references will be injected into the handler's variables scope. This is a huge performance benefit since event handlers are wired with all necessary dependencies upon creation instead of requesting dependencies (usage) at runtime. We encourage you to use injection whenever possible.

    triangle-exclamation

    Warning As a rule of thumb, inject only singletons into singletons. If not you can create unnecessary issues and memory leaks.

    You will achieve this in your handlers via property injection, which is the concept of defining properties in the component with a special annotation called inject, which tells WireBox what reference to retrieve via the . Let's say we have a users handler that needs to talk to a model called UserService. Here is the directory layout so we can see the conventions

    Here is the event handler code to leverage the injection:

    Notice that we define a cfproperty with a name and inject attribute. The name becomes the name of the variable in the variables scope and the inject annotation tells WireBox what to retrieve. By default it retrieves model objects by name and path.

    circle-check

    Tip: The is vast and elegant. Please refer to it. Also note that you can create object aliases and references in your : config/WireBox.cfc

    hashtag
    Requesting Model Objects

    The other approach to integrating with model objects is to request and use them as via the framework super type method: getInstance(), which in turn delegates to WireBox's getInstance() method. We would recommend requesting objects if they are transient (have state) objects or stored in some other volatile storage scope (session, request, application, cache, etc). Retrieving of objects is okay, but if you will be dealing with mostly singleton objects or objects that are created only once, you will gain much more performance by using injection.

    circle-info

    Association defines a relationship between classes of objects that allows one object instance to cause another to perform an action on its behalf. - 'wikipedia'

    hashtag
    A practical example

    In this practical example we will see how to integrate with our model layer via WireBox, injections, and also requesting the objects. Let's say that we have a service object we have built called FunkyService.cfc and by convention we will place it in our applications models folder.

    FunkyService.cfc

    Our funky service is not that funky after all, but it is simple. How do we interact with it? Let's build a Funky event handler and work with it.

    hashtag
    Injection

    By convention, I can create a property and annotate it with an inject attribute. ColdBox will look for that model object by the given name in the models folder, create it, persist it, wire it, and return it. If you execute it, you will get something like this:

    Great! Just like that we can interact with our model layer without worrying about creating the objects, persisting them, and even wiring them. Those are all the benefits that dependency injection and model integration bring to the table.

    hashtag
    Alternative wiring

    You can use the value of the inject annotation in several ways. Below is our recommendation.

    hashtag
    Requesting

    Let's look at the requesting approach. We can either use the following approaches:

    Via Facade Method

    Directly via WireBox:

    Both approaches do exactly the same thing. In reality getInstance() does a wirebox.getInstance() callback (Uncle Bob), but it is a facade method that is easier to remember. If you run this, you will also see that it works and everything is fine and dandy. However, the biggest difference between injection and usage can be seen with some practical math:

    As you can see, the best performance is due to injection as the handler object was wired and ready to roll, while the requested approach needed the dependency to be requested. Again, there are cases where you need to request objects such as transient or volatile stored objects.

    What's New With 6.2.x

    ColdBox 6.2.0 is a minor release with some major improvements in many areas like:

    • Async programming

    • Logging

    photos.show

    Show a photo by id

    PUT/PATCH

    /photos/:id

    photos.update

    Update a photo by id

    DELETE

    /photos/:id

    photos.delete

    Delete a photo by id

    - Remove a child injector by name
  • getChildInjector( name ) - Get a child injector by name

  • getChildInjectors() - Get all the child injectors registered

  • getChildInjectorNames() - Get an array of all the registered child injectors

  • Apart from the explicit lookup it can also do implicit hierarchical lookups using the following order:

    • Locally

    • Parent

    • All Children (in order of registration)

  • containsInstance( name ) - This method now also searches in the child collection for the specific name instance. The lookup searches in the following order:

    1. Locally

    2. Parent

    3. Children

  • shutdown() - The shutdown method has been enhanced to issue shutdown method calls to all child injectors registered.

  • COLDBOX-1066arrow-up-right Scheduled tasks not accessing application scope on Adobe Engines
  • COLDBOX-1063arrow-up-right ColdBox schedulers starting before the application is ready to serve requests

  • COLDBOX-1062arrow-up-right Scheduler service not registering schedulers with the appropriate name

  • COLDBOX-1051arrow-up-right scheduler names can only be used once - executor needs to be removed

  • COLDBOX-1036arrow-up-right Scheduled tasks fail after upgrading to coldbox 6.5. Downgrading to 6.4.0 works.

  • COLDBOX-1027arrow-up-right actions for a specific pattern cannot point to different handlers

  • Improvement

    • COLDBOX-1074arrow-up-right Improvements to module loading/activation log messages

    • COLDBOX-1071arrow-up-right Make unloadAll() in ModuleService more resilient by verifying loaded modules exist

    • COLDBOX-1061arrow-up-right Change default template cache from concurrentSoftReference to ConcurrentReference to avoid auto cleanups

    • Default route names to pattern when using route()

    • New router method: apiResources() to allow you to define resources without the new and edit actions

    • Update elixirPath to allow for many permutations of filenames and arguments to avoid cache collisions

    • Ability for the response setPagination() to use any incoming argument for storage

    • Move onRequestCapture after default event capture to allow for consistency on the capture

    • Deprecate declaration of multiple resources on a single resources() call

    • Improve routing DSL to allow for different HTTP verbs on the the same route to point to different events or actions

    New Feature

    • COLDBOX-1082arrow-up-right Announce onException interception points for async interceptors

    • COLDBOX-1080arrow-up-right experimental web mapping support to allow for modern app templates with assets outside of the webroot

    • COLDBOX-1076arrow-up-right Ability to pass in the domain to test executions in via integration testing

    • Enable automated full null support via github actions

    • ScheduledTask new getMemento() to get the state of the task

    • Schedulers can now get the current thread and thread name: getCurrentThread(), getThreadName() as private helpers

    • New controller method: getUserSessionIdentifier() which gives you the unique request tracking identifier according to our algorithms

    • New coldbox setting identifierProvider which can be a closure/udf/lambda that provides a unique tracking identifier for user requests

    Bug

    • CACHEBOX-76arrow-up-right Fixed method return value + SQL compatibility on jdbc metadata indexer thanks to @homestar9

    • CACHEBOX-75arrow-up-right reap operation was not ignoring 0 values for last access timeouts

    • Typo in queryExecute Attribute "datasource" in the JDBCStore.cfc

    Improvement

    • Replace IIF and urlEncodedFormat on cache content reports

    • Lower logging verbosity of cache reaping from info to debug messages

    Bug

    • WIREBOX-124arrow-up-right Killing IInjector interface usages due to many issues across cfml engines, leaving them for docs only

    • WIREBOX-118arrow-up-right Never override an existing variables key with virtual inheritance

    Improvement

    • DSLs process method now receives the caller targetID alongside the targetObject and the target definition

    New Feature

    • New wirebox DSL to inject the target's metadata that's cached in the target's binder: wirebox:objectMetadata

    • New WireBoxDSL: wirebox:targetID to give you back the target ID used when injecting the object

    Task

    • Removal of usage of Injector dsl interface due to so many issues with multiple engines

    GET

    /photos

    photos.index

    Get all photos

    POST

    /photos

    photos.create

    Create a photo

    GET

    cbdebuggerarrow-up-right
    domain and subdomain routingarrow-up-right
    COLDBOX-1072arrow-up-right
    COLDBOX-1069arrow-up-right
    WireBox Injector Hierarchy

    /photos/:id

    Object creations
  • WireBox Mappings and Binders

  • hashtag
    WireBox Binder & Mapping Objects Rewritten

    This is a major change in the core as we have finally rewritten the WireBox Binder and object Mapping objects to script. This resulted in a 45-50% code reduction for those objects and an impressive 30% speed improvements when creating and processing mappings with the new binder processing optimizations. We analyzed every single line of code on these two objects and we are incredibly satisfied with the initial results.

    That's right, we have some async goodness prepared for future versions when dealing with multiple directory mappings and much more.

    hashtag
    Async Package Performance Upgrades

    Fusion Reactor Profiler

    We have done more runtime analysis on our asynchronous package and we have optimized the heck out of it using the amazing FusionReactorarrow-up-right Profilers! Especially the applyAll() and collection based parallel computing. We reached a point where all of our tests cases where running faster than even native Lucee/Adobe 2021 parallel constructs. Below you can see a snapshot of our test bed creating 1000 transient objects with dependency injections and object populations using async constructs.

    Async Test Results

    You can find our test bed here: https://github.com/ColdBox/coldbox-platform/blob/development/tests/suites/async/performance-parallel-tests.cfmarrow-up-right

    Just a reminder that the ColdBox async capabilities are all powered by the Java concurrency packages leveraging Completable Futuresarrow-up-right, Executorsarrow-up-right and Scheduled Tasksarrow-up-right. It is not only true to the Java API but we have made tons of enhancements especially for ColdFusion and its dynamic nature. Check out our docs!

    It is also available to ANY ColdFusion application that is NOT running ColdBox. This is achieved by using either of our standalone libraries: WireBox, CacheBox and LogBox.

    hashtag
    Test Dependency Injection

    This version introduces the capability for you to tag your integration tests with an autowire annotation on the component tag. By adding this annotation, your test object will be inspected and wired with dependencies just like any other WireBox object.

    hashtag
    ColdBox Test Matchers

    We have also included a new object coldbox.system.testing.CustomMatchers which will register matchers into TestBox when doing integration tests. It will give you the nice ability to expect status codes and validation exceptions on RESTFul Requests via the ColdBox Response object.

    • toHaveStatus()

    • toHaveInvalidData()

    hashtag
    More Rendering Improvements

    Thanks to Dom Watson (@dom_watsonarrow-up-right) from PresideCMS (@presidecmsarrow-up-right) for many contributions to help clean up ColdBox view rendering! This release focuses on more performance and memory utilization updates, as well as refactoring external dependencies from pre singleton rendering approaches, which has resulted in more performance gains and lower memory usages on high rendering apps.

    hashtag
    Whoops! Keeps Getting Better

    We have had tons of updates and requests from our new exception handling experience in ColdBox: Whoops! In this release we tackle CFML core engine files so they can render appropriately, AJAX rendering for exceptions and best of all a huge performance and size boost when dealing with exceptions. Even when dealing with exceptions we want the best and the fastest experience possible for our developers.

    The previous approach whoops took was to read and load all the source code of all the templates that caused the exception. You could then navigate them to discover your faults. However, each template could be loaded from 1 to up to 10 times if the stack trace followed it. In this new update we provide source template caching and dynamic runtime injection and highlighting of the source code. This has granted us the following improvements in small test cases (Your improvements could be higher)

    Original Size: 218.54 KB New Size: 145.22 KB About 30-40% reduction on size depending on repetition of the templates

    Original Rendering Speed: 288ms New Rendering Speed: 76ms About 74-80% rendering improvements

    Whoops Improvements

    hashtag
    Release Notes

    hashtag
    6.2.0

    hashtag
    Bugs

    • [COLDBOX-936arrow-up-right] - ExceptionBean throws exception on weird ORM illegal access collection on scope dump

    • [] - Migration to cgi.server_name and server_port did not account for the incoming browser port but the cf service port

    • [] - getFullURL() is not accounting for app mappings

    • [] - Invalid event handler detection was overriding some event handler beans

    • [] - timeUnits had type mismatches when used in async futures' allApply

    • [] - Whoops should validate a file exists before trying to present it to the code viewer

    hashtag
    New Features

    • [] - Add timeout and timeUnit arguments to the allApply() method directly when using Futures

    • [] - New global settings: sesBasePath and HtmlBasePath that represent the pathing with no host and protocol

    hashtag
    Improvements

    • [] - migrating usage of cgi.http_host to cgi.server_name due to inconsistencies with proxy requests that affects caching and many other features

    • [] - ProcessStackTrace() Creates Many Nested Spans, improved output HTML

    hashtag
    Improvements

    • [] - Event caching now bases off the multi host key from the event.getSESBaseURL() to improve consistencies and single responsibility

    hashtag
    Bugs

    • [] - Missing line break on file appender control string

    hashtag
    Bugs

    • [] - parameter [binder] to function [process] is required but was not passed in When setting coldbox.autoMap to false and choosing either method of mapping a directory:

    hashtag
    6.2.1

    hashtag
    Bugs

    • [COLDBOX-960arrow-up-right] - jsonPayloadToRC is not working in 6.2 update

    • [] - Random bug dealing with integration testing when dealing with routes vs direct events

    hashtag
    Bugs

    • [] - Regression: Remove default dsl of "" from initArg() and addDIConstructorArgument()

    hashtag
    6.2.2

    hashtag
    Bugs

    • [COLDBOX-963arrow-up-right] - Use Java URI for more resiliant getFullURL to avoid double slashes

    hashtag
    Bugs

    • [] - wirebox metadata caching broken

    • [] - Standalone event pool interceptData -> data not backwards compat

    hashtag
    Improvements

    • [] - WireBox not handling cachebox, logbox, and asyncmanager instances properly

    hashtag
    Improvements

    • [] - CacheBox not handling wirebox, logbox, and asynmanager instances properly

    hashtag
    Improvements

    • [] - Ignore interrupted exceptions from appenders' scheduler pool

    WireBoxarrow-up-right
    http://forgebox.io/view/cbiocarrow-up-right
    WireBoxarrow-up-right
    scope-widening injectionarrow-up-right
    WireBox Injection DSL
    injection DSL
    config binderarrow-up-right
    associationsarrow-up-right
    apiResources( "users" );
    apiResources( "photos" );
    describe( "subdomain routing", function(){
    	beforeEach( function(){
    		setup();
    	} );
    	
    	it( "can match on a specific domain", function(){
    		var event = execute( route: "/", domain: "subdomain-routing.dev" );
    		var rc    = event.getCollection();
    		expect( rc ).toHaveKey( "event" );
    		expect( rc.event ).toBe( "subdomain.index" );
    	} );
    	
    	it( "skips if the domain is not matched", function(){
    		var event = execute( route: "/", domain: "not-the-correct-domain.dev" );
    		var rc    = event.getCollection();
    		expect( rc ).toHaveKey( "event" );
    		expect( rc.event ).toBe( "main.index" );
    	} );
    	
    	it( "can match on a domain with wildcards", function(){
    		var event = execute( route: "/", domain: "luis.forgebox.dev" );
    		var rc    = event.getCollection();
    		expect( rc ).toHaveKey( "event" );
    		expect( rc.event ).toBe( "subdomain.show" );
    	} );
    	
    	it( "provides any matched values in the domain in the rc", function(){
    		var event = execute( route: "/", domain: "luis.forgebox.dev" );
    		var rc    = event.getCollection();
    		expect( rc ).toHaveKey( "username" );
    		expect( rc.username ).toBe( "luis" );
    	} );
    } );
    coldbox : {
        identifierProvider : function(){
            if( isNull( cookie.mytracker ) ){
                cookie.myTracker = createUUID();
            }
            return cookie.myTracker;
        }
    }
    getInstance( name: "CategoryService", injector : "ChildInjector" )
    // Use the property name as the instance name
    property name="categoryService" inject="wirebox:child:childInjector"
    // Use a specific instance name
    property name="categoryService" inject="wirebox:child:childInjector:CategoryService"
    // Use any DSL
    property name="categoryService" inject="wirebox:child:childInjector:{DSL}"
    /**
     * Locates, Creates, Injects and Configures an object model instance
     *
     * @name The mapping name or CFC instance path to try to build up
     * @initArguments The constructor structure of arguments to passthrough when initializing the instance
     * @dsl The dsl string to use to retrieve the instance model object, mutually exclusive with 'name'
     * @targetObject The object requesting the dependency, usually only used by DSL lookups
     * @injector The child injector name to use when retrieving the instance
     */
    function getInstance(
    	name,
    	struct initArguments,
    	dsl,
    	targetObject = "",
    	injector
    );
    component extends="coldbox.system.testing.BaseTestCase" autowire {
    
        // DI
        property name="securityService" inject="provider:securityService";
        property name="jwt"             inject="provider:JWTService@cbsecurity";
        property name="cbsecure"        inject="provider:CBSecurity@cbsecurity";
        property name="qb"              inject="provider:QueryBuilder@qb";
    
        /*********************************** BDD SUITES ***********************************/
        function run(){
            describe( "Authentication Specs", function(){
                beforeEach( function( currentSpec ){
                    // Setup as a new ColdBox request for this suite, VERY IMPORTANT. ELSE EVERYTHING LOOKS LIKE THE SAME REQUEST.
                    setup();
                    // Make sure nothing is logged in to start our calls
                    securityService.logout();
                    jwt.getTokenStorage().clearAll();
                } );
    
                story( "I want to authenticate a user via username/password and receive a JWT token", function(){
                    given( "a valid username and password", function(){
                        then( "I will be authenticated and will receive the JWT token", function(){
                            // Use a user in the seeded db
                            var event = this.post(
                                "/api/v1/login",
                                {
                                    username : variables.testEmployeeEmail,
                                    password : variables.testPassword
                                }
                            );
                            var response = event.getPrivateValue( "Response" );
                            expect( response.getError() ).toBeFalse( response.getMessagesString() );
                            expect( response.getData() ).toHaveKey( "token,user" );
                            // debug( response.getData() );
                            var decoded = jwt.decode( response.getData().token );
                            expect( decoded.sub ).toBe( variables.testEmployeeId );
                            expect( decoded.exp ).toBeGTE( dateAdd( "h", 1, decoded.iat ) );
                            expect( response.getData().user.userId ).toBe( variables.testEmployeeId );
                        } );
                    } );
                    given( "invalid username and password", function(){
                        then( "I will receive a 401 invalid credentials exception ", function(){
                            var event = this.post(
                                "/api/v1/login",
                                { username : "invalid", password : "invalid" }
                            );
                            var response = event.getPrivateValue( "Response" );
                            expect( response.getError() ).toBeTrue();
                            expect( response.getStatusCode() ).toBe( 401 );
                        } );
                    } );
                } );
    
            } );
        }
    
    }
    var event = this.post(
        "/api/v1/login",
        { username : "invalid", password : "invalid" }
    );
    var response = event.getPrivateValue( "Response" );
    expect( response.getError() ).toBeTrue();
    expect( response.getStatusCode() ).toBe( 401 );
    
    var event = this.post(
        "api/v1/timelogs",
        { description : "A nice unit test" }
    );
    expect( event.getResponse() ).toHaveStatus( 400 );
    expect( event.getResponse() ).toHaveInvalidData( "project", "is required" )
    expect( event.getResponse() ).toHaveInvalidData( "task", "is required" )
    expect( event.getResponse() ).toHaveInvalidData( "user", "is required" )
    Directory Layout
    + handlers
      + users.cfc
    + models
      + UserService.cfc
    users.cfc
    component name="MyHandler"{
        
        // Dependency injection of the model: UserService -> variables.userService
        property name="userService" inject="UserService";
    
        function index( event, rc, prc ){
            prc.data = userService.list()
            event.setView( "users/index" );
        }
    
    }
    users.cfc
    component{
    
        function index( event, rc, prc ){
            // Request to use the user service, this would be best to inject instead 
            // of requesting it.
            prc.data = getInstance( "UserService" ).list();
            event.setView( "users/index" );
        }
        
        function save( event, rc, prc ){
            // request a user transient object, populate it and save it.
            prc.oUser = populateModel( getInstance( "User" ) );
            userService.save( prc.oUser );
            relocate( "users/index" );
        }
    
    }
    Directory Layout
     + application
      + models
         + FunkyService.cfc
    component singleton{
    
        function init(){
            return this;
        }
    
        function add(a,b){ 
            return a+b; 
        }
    
        function getFunkyData(){
            var data = [
                {name="Luis", age="33"},
                {name="Jim", age="99"},
                {name="Alex", age="1"},
                {name="Joe", age="23"}
            ];
            return data;
        }
    
    }
    component{
    
        // Injection via property
        property name="funkyService" inject="FunkyService";
    
        function index(event,rc,prc){
    
            prc.data = funkyService.getFunkyData();
    
            event.renderData( data=prc.data, type="xml" );
        }    
    
    
    }
    <array>
        <item>
            <struct>
                <name>Luis</name>
                <age>33</age>
            </struct>
        </item>
        <item>
            <struct>
                <name>Jim</name>
                <age>99</age>
            </struct>
        </item>
        <item>
            <struct>
                <name>Alex</name>
                <age>1</age>
            </struct>
        </item>
        <item>
            <struct>
                <name>Joe</name>
                <age>23</age>
            </struct>
        </item>
    </array>
    // Injection using the DSL by default name/id lookup
    property name="funkyService" inject="FunkyService";
    // Injection using the DSL id namespace
    property name="funkyService" inject="id:FunkyService";
    // Injection using the DSL model namespace
    property name="funkyService" inject="model:FunkyService";
    component{
    
        function index(event,rc,prc){
    
            prc.data = getInstance( "FunkyService" ).getFunkyData();
    
            event.renderData( data=prc.data, type="xml" );
        }    
    
    
    }
    component{
    
        function index(event,rc,prc){
    
            prc.data = wirebox.getInstance( "FunkyService" ).getFunkyData();
    
            event.renderData( data=prc.data, type="xml" );
        }    
    
    
    }
    1000 Requests made to users.index
    
    - Injection: 1000 handler calls + 1 model creation and wiring call = 1001 calls
    - Requesting: 1000 handler calls + 1000 model retrieval + 1 model creation call = 2002 calls
    ColdBox Site Quickstart
    WIREBOX-119arrow-up-right Missing coldbox:schedulerService DSL
  • WIREBOX-117arrow-up-right HDI - Ability for injectors to have a collection of child injectors to delegate lookups to, basically Hierarchical DI

  • COLDBOX-1056arrow-up-right
    COLDBOX-1050arrow-up-right
    COLDBOX-1049arrow-up-right
    COLDBOX-1048arrow-up-right
    COLDBOX-1037arrow-up-right
    COLDBOX-980arrow-up-right
    COLDBOX-676arrow-up-right
    COLDBOX-1073arrow-up-right
    COLDBOX-1065arrow-up-right
    COLDBOX-1064arrow-up-right
    COLDBOX-1033arrow-up-right
    COLDBOX-1032arrow-up-right
    CACHEBOX-74arrow-up-right
    CACHEBOX-73arrow-up-right
    CACHEBOX-79arrow-up-right
    WIREBOX-120arrow-up-right
    WIREBOX-122arrow-up-right
    WIREBOX-121arrow-up-right
    WIREBOX-123arrow-up-right
  • [COLDBOX-946arrow-up-right] - new request context method getFullPath() which returns the full url with no protocol or host

  • [COLDBOX-957arrow-up-right] - New autowire annotation or `variables.autowire` on integration tests so ColdBox will autowire the test with dependencies via WireBox

  • [COLDBOX-958arrow-up-right] - Store the test case metadata on ```variables.metadata``` so it can be reused by any helper within test operations

  • [COLDBOX-959arrow-up-right] - New ColdBox CustomMatchers object found at coldbox.system.testing.CustomMatchers which is loaded on all tests

  • [COLDBOX-933arrow-up-right] - Improved safety reset for base test cases

  • [COLDBOX-940arrow-up-right] - Performance optimizations for entire async package

  • [COLDBOX-944arrow-up-right] - Refactored cgi server and port detections to improve testability and single responsibiilty principles

  • [COLDBOX-945arrow-up-right] - Event caching now bases off the multi host key from the event.getSESBaseURL() to improve consistencies and single responsibility

  • [COLDBOX-947arrow-up-right] - encapsulate processEception() from the bootstrap to within the exception objects

  • [COLDBOX-949arrow-up-right] - better exception tracking for interceptor getProperty() calls that don't exist

  • [COLDBOX-952arrow-up-right] - RendererEncapsulator: use of filter method for rendererVariables is inefficient, migrated to less fluent but more performant approach.

  • [COLDBOX-953arrow-up-right] - Update DateFormat Mask to use lowercase "d" to be compatible with ACF2021

  • [COLDBOX-954arrow-up-right] - Refactor viewsHelperRef and layoutsHelperRef to local renderer variables instead of settings, which resulted in even better speed improvements

  • [COLDBOX-955arrow-up-right] - If in an Ajax request and an exception occurs using Whoops the view is unusable

  • [COLDBOX-956arrow-up-right] - Whoops loads multiple files into the DOM for the templates in the stacktrace causing major slowdownsa

  • [COLDBOX-953arrow-up-right] - Update DateFormat Mask to use lowercase "d" to be compatible with ACF2021
    hashtag
    New Features
    • [LOGBOX-57arrow-up-right] - new shutdown() method to process graceful shutdown of LogBox

    • [LOGBOX-58arrow-up-right] - New logbox config onShutdown() callback, which is called when LogBox has been shutdown

    • [] - New shutdown() method can be now used in appenders that will be called when LogBox is shutdown

    [WIREBOX-102arrow-up-right] - ACF incompats with future combinations due to dumb elvis operator bug

    hashtag
    New Features

    • [WIREBOX-98arrow-up-right] - Pass the current injector to the binder's life-cycle methods: onShutdown(), onLoad()

    • [WIREBOX-100arrow-up-right] - Create a processEagerInits() so it can process them at wirebox load

    • [] - Complete rewrite of the Mapping object to script and performance optimizations

    • [] - Complete rewrite of the WireBox Binder to script and optimizations

    • [] - New WireBox config: autoProcessMappings which can be used to auto process metadata inspections on startup.

    [WIREBOX-106arrow-up-right] - Regression: parentInjector() stopRecursions() collision with internal property name

    COLDBOX-937arrow-up-right
    COLDBOX-938arrow-up-right
    COLDBOX-939arrow-up-right
    COLDBOX-941arrow-up-right
    COLDBOX-948arrow-up-right
    COLDBOX-942arrow-up-right
    COLDBOX-943arrow-up-right
    COLDBOX-895arrow-up-right
    COLDBOX-931arrow-up-right
    COLDBOX-945arrow-up-right
    LOGBOX-56arrow-up-right
    WIREBOX-99arrow-up-right
    COLDBOX-961arrow-up-right
    WIREBOX-105arrow-up-right
    WIREBOX-107arrow-up-right
    WIREBOX-109arrow-up-right
    WIREBOX-108arrow-up-right
    CACHEBOX-65arrow-up-right
    LOGBOX-60arrow-up-right
    LOGBOX-59arrow-up-right
    WIREBOX-101arrow-up-right
    WIREBOX-103arrow-up-right
    WIREBOX-104arrow-up-right

    What's New With 6.7.0

    June 21, 2022

    ColdBox 6.7.x

    hashtag
    Major Updates

    Here is a listing of all the major updates and improvements in this version.

    hashtag
    Event Caching HTTP Response Codes

    Event caching has been updated to allow the caching of the set http response code from your handler code via event.setHTTPHeader() or event.renderData() . This is essential from a developer's perspective as it will respect whatever response code you respond with. This is also imperative for RESTFul services.

    hashtag
    WireBox Performance, Performance and More Performance

    This release brings in a complete re-architecture of the creation, inspection and wiring of objects in WireBox in order to increase performance. Every single line of code was optimized and analyzed in order to bring the creation, inspection and wiring of objects to its maximum speed. This will be noted more on the creation of transient (non-persisted) objects more than in singleton objects. So if you are asking WireBox for transient objects, you will see and feel the difference.

    In some of our performance testing we had about 4000 object instantiations running between 500ms-1,100 ms depending on CPU load. While with simple createObject() and no wiring, they click around 400-700 ms. Previously, we had the same instantiations clocking at 900-3,500 ms. So we can definitely see a major improvement in this area.

    hashtag
    New ColdBox Testing Virtual App

    This release sports the creation of the encapsulated virtual ColdBox App: coldbox.system.testing.VirtualApp. Previously, the only way to create virtual apps was to do it manually just like the BaseTestCase object did when doing integration testing and that's about it. In this release, we provide a clean interface for starting, restarting, checking and shutting down virtual applications that can be used for testing, proxying, etc. It also allows a faster and eager approach to starting the virtual application before your runner and tests. This will allow any ORM bridges to be respected and best of all the ability to execute anything in the framework before your runners, suites or specs. Database migrations anyone?

    hashtag
    Test Harness

    All application templates have been updated to use the new Virtual App in the tests/Application.cfc. This will allow for a virtual application to be started once your runner or specs are executed and shutdown at the end of the request. Here are the two methods in charge of doing this in the Application.cfc

    Your integration tests and unit tests remain the same. The only difference is that internally they all use this object to create the virtual applications.

    circle-exclamation

    Also note the code on line 8. This allow the developer to decide when then virtual app starts.

    hashtag
    Scheduled Tasks Exception Handling

    All scheduled tasks have automatic exception handling now. Before, whenever you had exceptions and you did NOT implement any of the exception handling listeners, your code would be swallowed up and never to be seen! Now, we avoid this and log them to standard error so you can debug your code.

    Also, in the previous version, if you had an exception your afterAnyTask() or the after() life cycle methods would never be called. Now they are!! Hooray!!

    hashtag
    ColdBox Schedulers Automatic Injection

    All ColdBox enabled schedulers will have the following automatic injections so you can have ease of use for leveraging objects and contexts during your task declarations and executables.

    All module schedulers will have the following extra automatic injections:

    hashtag
    Scheduled Tasks Start and End Dates

    All scheduled tasks now support the ability to seed in the start and end dates via our DSL:

    • startOn( date, time = "00:00" )

    • endOn( data, time = "00:00" )

    This means that you can tell the scheduler when the task will become active on a specific data and time (using the scheduler's timezone), and when the task will become disabled.

    hashtag
    xTask() - Easy Disabling of Tasks

    Thanks to the inspiration of where you can mark a spec or test to be skipped from execution by prefixing it with the letter x you can now do the same for any task declaration. If they are prefixed with the letter x they will be registered but disabled automatically for you.

    hashtag
    Scheduled Tasks Singular Time Units

    Every time unit can now be used as plural or singular, so it can allow you to create beautiful scheduled task DSLs:

    hashtag
    Available Time Units

    • Nanosecond(s)

    • Microsecond(s)

    • Millisecond(s)

    hashtag
    Safe Shutdown of Executors and Schedulers

    All executors and schedulers can now be shutdown more gracefully by passing a timeout argument to the async manager and automatically by the framework. This will allow the executor to shutdown and gracefully yell at it's tasks to shutdown in a default period of 30 seconds. It will wait and then try again, if not, then well, you can't directly kill anything anymore, so you will be notified so you can do harsher punishments to these tasks.

    The only exception this is NOT the case in a normal ColdBox app is when a reinit happens or when integration testing is being executed. Then we revert to the previous behavior of nuking the executors and schedulers.

    AsyncManager

    • shutdownAllExecutors( force, timeout )

    • shutdownExecutor( name, force, timeout)

    The timeout ONLY works when the force argument is false. If force is true, then it's NOT gracefully shutdown. This usually happens on ColdBox reinits or integration testing.

    Schedulers

    All schedulers have a shutdownTimeout property that defaults to 30 seconds. When you configure your schedulers you can change this value to whatever you see fit.

    Executors - shutdown and wait...

    All executors now have a new method: shutdownAndAwaitTermination( numeric timeout = 30 ) which is used to do just that. It places the executor in shutdown mode and waits the timeout for all the tasks to complete. If they don't complete then it issues a forced shutdown.

    hashtag
    forAttribute() - Integrate with JS Frameworks Easily

    All handlers/layouts and views get a new function called forAttribute( data )which will allow you to serialize simple or complex data so it can be used within HTML attributes. This will take care of serialize the data and encoding it correctly so it can be bound to the HTML attribute so the JavaScript framework can use it as native JSON.

    This technique will allow you to bridge your CFML apps with your JS apps natively.

    hashtag
    Async Interceptors Data

    Finally in this version of ColdBox asynchronous interceptors will work with any complex data without any thread contingency or duplication. You can even use them for ORM events and it will work accordingly. So go for it, your interception calls!

    hashtag
    ORM Event Handling

    This was a rough regression due to the way Hibernate is loaded by the CFML engines. We have moved to a lazy load first strategy on the entire architecture of the framework. So anything using the ColdBox proxy, like the ORM event handling, will now work in any loading situation.

    hashtag
    Release Notes

    Bug

    • Persistence of variables failing due to null support

    • Renderer is causing coldbox RestHandler to render convention view

    The ColdBox app mapping

    getJavaSystem()

    Function to get access to the java system

    getSystemSetting()

    Retrieve a Java System property or env value by name. It looks at properties first then environment variables

    getSystemProperty()

    Retrieve a Java System property value by key

    getEnv()

    Retrieve a Java System environment value by name

    Second(s)
  • Minute(s)

  • Hour(s)

  • Day(s)

  • COLDBOX-1109arrow-up-right Exceptions in async interceptors are missing onException announcement
  • COLDBOX-1105arrow-up-right Interception with async annotation causes InterceptorState Exception on Reinit

  • COLDBOX-1104arrow-up-right A view not set exception is thrown when trying to execution handler ColdBox methods that are not concrete actions when they should be invalid events.

  • COLDBOX-1103arrow-up-right Update getServerIP() so it avoids looking at the cgi scope as it can cause issues on ACF

  • COLDBOX-1100arrow-up-right Event Caching Does Not Preserve HTTP Response Codes

  • COLDBOX-1099arrow-up-right Regression on ColdBox v6.6.1 around usage of statusCode = 0 on relocates

  • COLDBOX-1098arrow-up-right RequestService context creation not thread safe

  • COLDBOX-1097arrow-up-right Missing scopes on isNull() checks

  • COLDBOX-1092arrow-up-right RestHandler Try/Catches Break In Testbox When RunEvent() is Called

  • COLDBOX-1045arrow-up-right Scheduled tasks have no default error handling

  • COLDBOX-1043arrow-up-right Creating scheduled task with unrecognized timeUnit throws null pointer

  • COLDBOX-1042arrow-up-right afterAnyTask() and task.after() don't run after failing task

  • COLDBOX-1040arrow-up-right Error in onAnyTaskError() or after() tasks not handled and executor dies.

  • COLDBOX-966arrow-up-right Coldbox Renderer.RenderLayout() Overwrites Event's Current View

  • Improvement

    • COLDBOX-1124arrow-up-right Convert mixer util to script and utilize only the necessary mixins by deprecating older mixins

    • COLDBOX-1116arrow-up-right Enhance EntityNotFound Exception Messages for rest handlers

    • COLDBOX-1096arrow-up-right SES is always disabled on RequestContext until RoutingService request capture : SES is the new default for ColdBox Apps

    • coldbox 6.5 and 6.6 break ORM event handling in cborm

    • Scheduled Tasks: Inject module context variables to module schedulers and inject global context into global scheduler

    • Create singular aliases for timeunits

    New Feature

    • COLDBOX-1123arrow-up-right New xTask() method in the schedulers that will automatically disable the task but still register it. Great for debugging!

    • COLDBOX-1121arrow-up-right Log schedule task failures to console so errors are not ignored

    • COLDBOX-1120arrow-up-right Scheduler's onShutdown() callback now receives the boolean force and numeric timeout arguments

    • The Scheduler's shutdown method now has two arguments: boolean force, numeric timeout

    • All schedulers have a new property: shutdownTimeout which defaults to 30 that can be used to control how long to wait for tasks to gracefully complete when shutting down.

    • New coldobx.system.testing.VirtualApp object that can startup,restart and shutdown Virtual Testing Applications

    • Async interceptos can now discover their announced data without duplicating it via cfthread

    • Interception Event pools are now using synchronized linked maps to provide concurrency

    • New super type function "forAttribute" to help us serialize simple/complex data and encoded for usage in html attributes

    • announce onException interception from RESTHandler, when exceptions are detected

    • Async schedulers and executors can now have a graceful shutdown and await for task termination with a configurable timeout.

    • Scheduled tasks add start and end date/times

    Task

    • COLDBOX-1122arrow-up-right lucee async tests where being skipped due to missing engine check

    • COLDBOX-1117arrow-up-right Remove nextRun stat from scheduled task, it was never implemented

    Bug

    • CACHEBOX-66arrow-up-right Cachebox concurrent store meta index not thread safe during reaping

    Improvement

    • CACHEBOX-82arrow-up-right Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load

    Improvement

    • LOGBOX-68arrow-up-right Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load

    • LOGBOX-65arrow-up-right File Appender missing text "ExtraInfo: "

    Bug

    • WIREBOX-126arrow-up-right Inherited Metadata Usage - Singleton attribute evaluated before Scopes

    Improvement

    • WIREBOX-129arrow-up-right Massive refactor to improve object creation and injection wiring

    • Injector now caches all object contains lookups to increase performance across hierarchy lookups

    • Lazy load all constructs on the Injector to improve performance

    • Remove the usage of identity hash codes, they are no longer relevant and can cause contention under load

    Property

    Description

    controller

    The ColdBox running app controller

    cachebox

    The CacheBox reference

    wirebox

    The WireBox reference

    log

    A configured LogBox logger

    coldboxVersion

    The ColdBox version you are running

    Property

    Description

    moduleMapping

    The module’s mapping

    modulePath

    The module’s path on disk

    moduleSettings

    The module’s settings structure

    TestBoxarrow-up-right
    asynchronize
    COLDBOX-1114arrow-up-right
    COLDBOX-1110arrow-up-right
    coldbox.system.testing.VirtualApp
    Vue.JS, Alpine, React, Angular, etc.

    appMapping

    GitHub - coldbox-templates/modern: A fresh new modern approach to a base templateGitHubchevron-right
    function data( event, rc, prc ) cache=true cacheTimeout=5{
        
        event.setHTTPHeader( statusCode = 404 );
    
    }
    public boolean function onRequestStart( targetPage ){
    	// Set a high timeout for long running tests
    	setting requestTimeout="9999";
    	// New ColdBox Virtual Application Starter
    	request.coldBoxVirtualApp = new coldbox.system.testing.VirtualApp( appMapping = "/root" );
    
    	// If hitting the runner or specs, prep our virtual app
    	if ( getBaseTemplatePath().replace( expandPath( "/tests" ), "" ).reFindNoCase( "(runner|specs)" ) ) {
    		request.coldBoxVirtualApp.startup();
    	}
    
    	// ORM Reload for fresh results
    	if( structKeyExists( url, "fwreinit" ) ){
    		if( structKeyExists( server, "lucee" ) ){
    			pagePoolClear();
    		}
    		// ormReload();
    		request.coldBoxVirtualApp.restart();
    	}
    
    	return true;
    }
    
    public void function onRequestEnd( required targetPage ) {
    	request.coldBoxVirtualApp.shutdown();
    }
    task( "restricted-task" )
      .call( () => ... )
      .everyHour()
      .startOn( "2022-01-01", "00:00" )
      .endOn( "2022-04-01" )
    function configure(){
    
    	xtask( "Disabled Task" )
    		.call ( function(){
    			writeDump( var="Disabled", output="console" );
    		})
    		.every( 1, "second" );
    
    	task( "Scope Test" )
    		.call( function(){
    			writeDump( var="****************************************************************************", output="console" );
    			writeDump( var="Scope Test (application) -> #getThreadName()# #application.keyList()#", output="console" );
    			writeDump( var="Scope Test (server) -> #getThreadName()# #server.keyList()#", output="console" );
    			writeDump( var="Scope Test (cgi) -> #getThreadName()# #cgi.keyList()#", output="console" );
    			writeDump( var="Scope Test (url) -> #getThreadName()# #url.keyList()#", output="console" );
    			writeDump( var="Scope Test (form) -> #getThreadName()# #form.keyList()#", output="console" );
    			writeDump( var="Scope Test (request) -> #getThreadName()# #request.keyList()#", output="console" );
    			writeDump( var="Scope Test (variables) -> #getThreadName()# #variables.keyList()#", output="console" );
    			writeDump( var="****************************************************************************", output="console" );
    		} )
    		.every( 60, "seconds" )
    		.onFailure( function( task, exception ){
    			writeDump( var='====> Scope test failed (#getThreadName()#)!! #exception.message# #exception.stacktrace.left( 500 )#', output="console" );
    		} );
    		
    }
    // Before
    task( "my-task" )
    	.call( () => {} )
    	.every( 1, 'hours' );
    
    // Which sounds weird when you read it. So now you can use singular
    
    task( "my-task" )
    	.call( () => {} )
    	.every( 1, 'hour' );
    function configure(){
        
        // Set shutdown timeout to 60 seconds
        setShutdownTimeout( 60 );
    
    }
    <div>
        <User :data="#forAttribute( prc.user.getMemento() )#">
    </div>
    threadData = announce(
        state           = "onPageCreate", 
        data            = { page= local.page }, 
        asyncAll        = true,
        asyncPriority   = "high"
    );
    COLDBOX-1094arrow-up-right
    COLDBOX-1067arrow-up-right
    COLDBOX-1044arrow-up-right
    COLDBOX-1119arrow-up-right
    COLDBOX-1118arrow-up-right
    COLDBOX-1113arrow-up-right
    COLDBOX-1108arrow-up-right
    COLDBOX-1107arrow-up-right
    COLDBOX-1106arrow-up-right
    COLDBOX-1101arrow-up-right
    COLDBOX-1053arrow-up-right
    COLDBOX-1052arrow-up-right
    WIREBOX-128arrow-up-right
    WIREBOX-127arrow-up-right
    WIREBOX-125arrow-up-right
    :fire:

    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.

    hashtag
    Engine Support

    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.

    hashtag
    Asynchronous Programming

    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 section to start delving into what we are lovingly calling cbFutures!

    hashtag
    ColdBox Executors & Tasks

    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.

    hashtag
    What are ColdBox Futures?

    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.

    circle-info

    See

    This new approach to creating async pipelines and parallel processing, will further create extensibility and robustness in your ColdBox applications.

    hashtag
    coldbox-tasks Global Executor

    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.

    hashtag
    CacheBox Executors

    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.

    hashtag
    Logging Enhancements

    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.

    hashtag
    Whoops! Modern Exception Handling

    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.

    triangle-exclamation

    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:

    Valid Exception Editors are:

    • vscode (Default)

    • vscode-insiders

    • sublime

    hashtag
    RestHandler & ColdBox Response

    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

    hashtag
    Base Class

    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.

    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:

    hashtag
    AroundHandler in Detail

    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

    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

    hashtag
    RequestContext Additions

    • getResponse()

      • Will get you the current prc.response object, if the object doesn’t exist, it will create it and set it for you

    hashtag
    Extending The RestHandler

    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.

    hashtag
    Extending The Response Object

    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.g

    and add the following method:

    Don't forget to register your interceptor in config/Coldbox.cfc:

    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.

    hashtag
    Functional If's using when()

    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:

    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.

    hashtag
    ColdBox Renderer Becomes a Singleton

    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!

    hashtag
    Custom ReinitKey

    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.

    Then you can use it in the request: http://localhost/index.cfm?cbReinit=true

    hashtag
    LogBox Config Path Init

    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.

    hashtag
    AnnounceInterception(), processState() Deprecated

    These methods have been deprecated in favor of our new announce() method. We have also deprecated the argument interceptData in favor of just data.

    hashtag
    New listen() method to register one-off closures

    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:

    This allows you to easily register dynamic closures/udfs whenever you like so they can listen to events:

    hashtag
    New Interception: onColdBoxShutdown()

    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.

    hashtag
    Routing Enhancements

    We have done several enhancements to the entire routing capabilities in ColdBox apart from several bug fixes.

    hashtag
    buildLink() Ease of Use

    We have also re-arranged the arguments so you can easily build links with query strings using positional arguments instead of name-value pairs:

    hashtag
    buildLink() Named Route Support

    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.

    hashtag
    Route Metadata

    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.

    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:

    hashtag
    Route Record

    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:

    hashtag
    Dynamic Routing Redirection

    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.

    This is great if you need to actually parse the incoming route and do a dynamic relocation.

    Happy Redirecting!

    hashtag
    Release Notes

    The full release notes per library can be found below. Just click on the library tab and explore their release notes:

    hashtag
    Bugs

    • [] - CacheBox creates multiple reap threads if the initial one take longer to complete than the reap frequency

    :fire:
    Logo
  • textmate

  • emacs

  • macvim

  • idea

  • atom

  • espresso

  • Error handling
  • Invalid Route handling

  • Much more

  • 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.

    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 CF

      • x-cached-response : If the request is cached via event caching

  • The core response object can be found here: coldbox.system.web.context.Response
    [COLDBOX-339arrow-up-right] - Error in AbstractFlashScope: key does't exists due to race conditions
  • [COLDBOX-822arrow-up-right] - InvalidEvent is not working when set to a module event

  • [COLDBOX-829arrow-up-right] - Stopgap for Lucee bug losing sessionCluster application setting

  • [COLDBOX-832arrow-up-right] - toResponse() silently fails on incorrect data types

  • [COLDBOX-837arrow-up-right] - Unable to manually call invalid event method without producing error

  • [COLDBOX-839arrow-up-right] - Router method and argument name discrepancy

  • [COLDBOX-845arrow-up-right] - Capture request before announcing onRequestCapture

  • [COLDBOX-850arrow-up-right] - XML Converter Updated invoke() to correctly call method by name

  • [COLDBOX-857arrow-up-right] - ElixirPath does not take in account of module root

  • [COLDBOX-861arrow-up-right] - Self-autowire fails for applications with context root configured in ColdBox Proxy

  • [COLDBOX-862arrow-up-right] - when passing custom cfml executors to futures it blows up as the native executor is not set

  • [COLDBOX-873arrow-up-right] - NullPointerException in ScheduledExecutor (Lucee 5.3.4.80)

  • [COLDBOX-875arrow-up-right] - PopulateFromQuery : Gracefully handle out of index rownumber in populateFromQuery #450

  • [COLDBOX-878arrow-up-right] - ColdBox 6 blows up if models directory doesn't exist

  • [COLDBOX-879arrow-up-right] - Reinit-Password-Check does not use the new "reinitKey"-Setting

  • [COLDBOX-880arrow-up-right] - ViewHelpers not working in CB-6 RC

  • [COLDBOX-885arrow-up-right] - Pagination not showing from rest response

  • [COLDBOX-889arrow-up-right] - RendererEncapsulator passes view-variables to "next" rendered view

  • [COLDBOX-891arrow-up-right] - Whoops breaking on some exceptions

  • [COLDBOX-897arrow-up-right] - Template cache eventSnippets don't match module events or event suffixes

  • [COLDBOX-899arrow-up-right] - queryString argument ignored when using event in `BaseTestCase#execute`

  • [COLDBOX-903arrow-up-right] - Renderer.ViewNotSetException when renderLayout used in request

  • [COLDBOX-911arrow-up-right] - Garbled text in Whoops error screen - utf8 encoding

  • hashtag
    New Features

    • [COLDBOX-268arrow-up-right] - Async Workers

    • [COLDBOX-749arrow-up-right] - Performance: make renderer a singleton

    • [COLDBOX-848arrow-up-right] - Improve the bug reporting template for development based on whoops

    • [] - Incorporate Response and RestHandler into core

    • [] - All ColdBox apps get a `coldbox-tasks` scheduler executor for internal ColdBox services and scheduled tasks

    • [] - Updated the default ColdBox config appender to be to console instead of the dummy one

    • [] - ColdBox controller gets a reference to the AsyncManager and registers a new `AsyncManager@coldbox` wirebox mapping

    • [] - Allow for the application to declare it's executors via the new `executors` configuration element

    • [] - Allow for a module to declare it's executors via the new `executors` configuration element

    • [] - Introduction of async/parallel programming via cbPromises

    • [] - ability to do async scheduled tasks with new async cbpromises

    • [] - Convert proxy to script and optimize it

    • [] - Add setting to define reinit key vs. hard-coded fwreinit: reinitKey

    • [] - jsonPayloadToRC now defaults to true

    • [] - autoMapModels defaults to true now

    • [] - RequestContext Add urlMatches to match current urls

    • [] - Response, SuperType => New functional if construct when( boolean, success, failure )

    • [] - Removed fwsetting argument from getSetting() in favor of a new function: getColdBoxSetting()

    • [] - BaseTestCase new method getHandlerResults() to easy get the handler results, also injected into test request contexts

    • [] - New dsl coldbox:coldboxSettings alias to coldbox:fwSettings

    • [] - New dsl coldbox:asyncManager to get the async manager

    • [] - Elixir manifest support for module and app roots via discovery

    • [] - New listen() super type and interceptor service method to register one-off closures on specific interception points

    • [] - The buildLink( to ) argument can now be a struct to support named routes : { name, params }

    • [] - Move queryString as the second argument for buildLink() so you can use it with psoitional params

    • [] - New context method: getCurrentRouteRecord() which gives you the full routed route record

    • [] - New context method: getCurrentRouteMeta() which gives you the routed route metadata if any

    • [] - New router method: meta() that you can use to store metadata for a specific route

    • [] - Every route record can now store a struct of metadata alongside of it using the `meta` key

    • [] - Allow toRedirect() to accept a closure which receives the matched route, you can process and then return the redirect location

    • [] - New onColdBoxShutdown interception point fired when the entire framework app is going down

    hashtag
    Tasks

    • [COLDBOX-866arrow-up-right] - onInvalidEvent is now removed in favor of invalidEventHandler, this was deprecated in 5.x

    • [COLDBOX-867arrow-up-right] - Removed interceptors.SES as it was deprecated in 5

    • [COLDBOX-870arrow-up-right] - setnextEvent removed as it was deprecated in 5

    • [] - getModel() is now fully deprecated and removed in fvor of getInstance()

    • [] - elixir version 2 support removed

    • [] - `request` and associated integration test methods are not in the official docs

    hashtag
    Improvement

    • [COLDBOX-830arrow-up-right] - Update cachebox flash ram to standardize on unique key discovery

    • [COLDBOX-833arrow-up-right] - Improvements to threading for interceptors and logging to avoid dumb Adobe duplicates

    • [COLDBOX-841arrow-up-right] - Change announceInterception() and processState() to a single method name like: announce()

    • [] - Use relocate and setNextEvent status codes in getStatusCode for testing integration

    • [] - Deprecate interceptData in favor of just data

    • [] - Please add an easily accessible "fwreinit" button to whoops...

    • [] - migrating usage of cgi.http_host to cgi.server_name due to inconsistencies with proxy requests that affects caching and many other features

    • [] - Interceptor Buffer Methods Removed

    • [] - Better module registration/activation logging to identify location and version

    hashtag
    Bugs

    • [WIREBOX-90arrow-up-right] - Fix constructor injection with virtual inheritance

    hashtag
    New Features

    • [] - Injector's get a reference to an asyncManager and a task scheduler whether they are in ColdBox or non-ColdBox mode

    • [] - New `executors` dsl so you can easily inject executors ANYWEHRE

    • [] - New dsl coldbox:coldboxSetting:{setting} alias to coldbox:fwSetting:{setting}

    hashtag
    Improvements

    • [] - Improve WireBox error on Adobe CF

    • [] - Rename WireBox provider get() to $get() to avoid conflicts with provided classes

    • [] - getInstance() now accepts either dsl or name via the first argument and initArguments as second argument

    hashtag
    Bugs

    • [CACHEBOX-59arrow-up-right] - Announced Events in the set() of the cacheBoxProvider

    • [] - cfthread-20506;variable [ATTRIBUES] doesn't exist;lucee.runtime.exp.ExpressionException: variable [ATTRIBUES] doesn't exist

    hashtag
    New Features

    • [] - CacheBox reaper : migrate to a scheduled task via cbPromises

    • [] - CacheFactory gets a reference to an asyncManager and a task scheduler whether they are in ColdBox or non-ColdBox mode

    hashtag
    Improvements

    • [] - Migrations to script and more fluent programming

    hashtag
    Bugs

    • [LOGBOX-35arrow-up-right] - FileAppender: if logging happens in a thread, queue never gets processed and, potentially, you run out of heap space

    • [] - Rotate property is defined but never used

    • [] - Work around for adobe bug CF-4204874 where closures are holding on to tak contexts

    • [] - Rolling file appender inserting tabs on first line

    hashtag
    New Features

    • [] - Allow config path as string in LogBox init (standalone)

    • [] - Allow standard appenders to be configured by name (instead of full path)

    • [] - Added an `err()` to abstract appenders for reporting to the error streams

    hashtag
    Improvements

    • [] - Improvements to threading for logging to avoid dumb Adobe duplicates

    • [] - refactoring of internal utility closures to udfs to avoid ACF memory leaks: CF-4204874

    • [] - Migrations to script and more fluent programming

    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

    Async Programming
    https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.htmlarrow-up-right
    COLDBOX-48arrow-up-right
    ColdBox 'async' package
    Whoops! Exception Template
    RestHandler UML
    Singleton Renderer
    Route Record

    onMissingAction()

    // 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() ) );
    asyncManager.getExecutor( "coldbox-tasks" )
    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
    }
    config/ColdBox.cfc
    function development() {
        coldbox.exceptionEditor = "vscode";
    }
    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" );
      }
    
    }
    // BaseHandler
    component extends="coldbox.system.Resthandler"{
    
      // Modify it here
    
    }
    
    // Then make your own handlers extend from it
    component extends="BaseHandler"{
    
    }
    coldbox create interceptor name=MyInterceptor points=preProcess
    function preProcess( event, interceptData, rc, prc ){
      prc.response = wirebox.getInstance( "MyResponseObject" );
    }
    		interceptors = [
    			{
    			class      : "interceptors.MyInterceptor",
    				name       : "MyInterceptor",
    				properties : {}
    			}
    		];
    /**
     * 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 )
    function save( event, rc, prc ){
        var oUser = populateModel( "User" );
        
        when( hasAccess(), () => oUser.setRole( rc.role ) );
    
    }
    coldbox.reinitKey = "cbReinit";
    application.logbox = new LogBox();
    
    application.logbox = new LogBox( "config.MyLogBox" );
    // New consistent method
    announce( state, data );
    
    // Usage
    announce( "myCustomEvent", { data = this } );
    /**
     * 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 )
    // 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" );
    function onColdBoxShutdown(){
        myRabbitMQChannel.close();
    }
    string function buildLink(
        to,
        queryString       = "",
        boolean translate = true,
        boolean ssl,
        baseURL     = ""
    );
    
    <a href="#event.buildLink( 'main.list', 'userid=4' )#">My Link</a>
    // 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 } } )
    config/Router.cfc
    route( "/render/:format" )
    	.meta( { secure : false } )
    	.to( "actionRendering.index" );
    
    // Resources
    	resources(
    		resource: "photos",
    		meta    : { secure : true }
    	);
    if( event.getCurrentRouteMeta().secure ){
      // secure it.
    }
    ( route, params, event ) => "/new/route" 
    
    function( route, params, event ){ return "/new/route"; }
    route( "/old/api/users/:id" )
        .toRedirect( ( route, params, event ) => { return "/api/v1/users/#params.id#" } )

    [] - All appenders get a reference to the running LogBox instance

  • [] - LogBox has a scheduler executor and the asyncmanager attached to it for standalone and ColdBox mode.

  • [] - Rolling appender now uses the new async schedulers to stream data to files

  • [] - Update ConsoleAppender to use TaskScheduler

  • [] - AbstractAppender log listener and queueing facilities are now available for all appenders

  • [] - DB Appender now uses a queueing approach to sending log messages

  • [] - Rolling File Appender now uses the async scheduler for log rotation checks

  • COLDBOX-849arrow-up-right
    COLDBOX-851arrow-up-right
    COLDBOX-852arrow-up-right
    COLDBOX-853arrow-up-right
    COLDBOX-855arrow-up-right
    COLDBOX-856arrow-up-right
    COLDBOX-858arrow-up-right
    COLDBOX-859arrow-up-right
    COLDBOX-860arrow-up-right
    COLDBOX-863arrow-up-right
    COLDBOX-864arrow-up-right
    COLDBOX-865arrow-up-right
    COLDBOX-868arrow-up-right
    COLDBOX-869arrow-up-right
    COLDBOX-871arrow-up-right
    COLDBOX-874arrow-up-right
    COLDBOX-876arrow-up-right
    COLDBOX-877arrow-up-right
    COLDBOX-887arrow-up-right
    COLDBOX-894arrow-up-right
    COLDBOX-905arrow-up-right
    COLDBOX-906arrow-up-right
    COLDBOX-907arrow-up-right
    COLDBOX-908arrow-up-right
    COLDBOX-909arrow-up-right
    COLDBOX-910arrow-up-right
    COLDBOX-912arrow-up-right
    COLDBOX-915arrow-up-right
    COLDBOX-872arrow-up-right
    COLDBOX-886arrow-up-right
    COLDBOX-900arrow-up-right
    COLDBOX-846arrow-up-right
    COLDBOX-882arrow-up-right
    COLDBOX-892arrow-up-right
    COLDBOX-895arrow-up-right
    COLDBOX-904arrow-up-right
    COLDBOX-916arrow-up-right
    WIREBOX-91arrow-up-right
    WIREBOX-92arrow-up-right
    WIREBOX-97arrow-up-right
    WIREBOX-88arrow-up-right
    WIREBOX-93arrow-up-right
    WIREBOX-94arrow-up-right
    CACHEBOX-63arrow-up-right
    CACHEBOX-24arrow-up-right
    CACHEBOX-60arrow-up-right
    CACHEBOX-64arrow-up-right
    LOGBOX-38arrow-up-right
    LOGBOX-45arrow-up-right
    LOGBOX-50arrow-up-right
    LOGBOX-5arrow-up-right
    LOGBOX-11arrow-up-right
    LOGBOX-36arrow-up-right
    LOGBOX-37arrow-up-right
    LOGBOX-41arrow-up-right
    LOGBOX-51arrow-up-right
    LOGBOX-42arrow-up-right
    LOGBOX-43arrow-up-right
    LOGBOX-44arrow-up-right
    LOGBOX-46arrow-up-right
    LOGBOX-47arrow-up-right
    LOGBOX-48arrow-up-right
    LOGBOX-49arrow-up-right
    Server, proxy
    Performance, stopwatch, timer, speed, time, time management
    Calendar, date, event, month