WOW! This one took a while but finally our M4 release is now available. So is this a final release, beta, or what? Well, we are taking off the beta name and will use our Milestone release as it is now deemed production ready even though I see a lot of users already running production sites with 3.0.0 Beta. Anyways, this milestone release has tons of fixes and updates in preparation for our next milestone which we should introduce in the coming months in preparation for a Release Candidate in Mid or Late April. Our milestones are evolving as planned and our core is getting more solid and solid with each release. This one especially really tightens the bolts on a lot of features introduced since the first betas. So let's start looking at what we did. However, please look at the tickets that have a (COMPATIBILITY) tag on them, if they do, then this means that this release has changed behavior from previous releases and you must see if you use that particular piece of code because most likely it needs to be updated. Please bear with us as all our docs are been reviewed for updates and more are added every day. Our new wiki is also online, powered by CodexWiki of course, and it will contain only 3.0.0 documentation. The 2.6.4 and less docs will remain in the orginal location. Here are the tickets for this milestone:
The major updates we will discuss, the rest are fixes that you can read the ticket notes if you are interested.
New ColdBox.cfc for configurations, NO MORE XML!
You heard right, no more XML in ColdBox. You can now configure the entire application using our new ColdBox.cfc which is a simple CFC that you place in your config folder with some cool methods and variables you set. No need to extend or anything, we mix it for you and it is about 5 times faster to load an application with this approach than with XML. Not only that, but we added environment detection built in, no more environments xml or interceptor. So how does it work. Well, you have to create a method called configure() which is called to configure your application. Inside of this method you can set the following elements:
- coldbox : struct of coldbox settings
- settings : struct of your settings
- conventions : struct of custom conventions
- environments : struct of environment patterns to listen to
- ioc : struct of ioc factories
- models : struct of model settings
- debugger : struct of debugger settings
- mailSettings : struct of mail settings
- i18n : struct of i18n settings
- bugTracers : struct of bug tracer reports
- webservices : struct of webservices
- datasources : struct of datasources
- layoutSettings : struct of layout settings
- layouts : struct of layout definitions
- cacheEngine : the cache engine settings
- interceptorSettings : the main interceptor directives
- interceptors : an array of interceptor declarations
We also create some objects and variables in the object's variables scope:
- controller : A reference to the coldbox controller
- AppMapping : The AppMapping of the application
- logBoxConfig : The logBox configuration object so you can configure the logging for your application
You can also create a method called: detectEnvironment() that if declared the framework will call it to figure out on what environment you are on. You only return a string with the name of the environment. The framework will then look for a method of the same name as your environment can call it! Cool! So if you do this:
environments = {
// The dev environment is used whenever the cgi.http_host begins with cf8 or railo
dev = "^cf8,^railo"
};
function dev(){
//override or declare new stuff here
}
That's it, if the dev environment is detected, then the dev() method is called. So here is a sample config:
/**
structures to create for configuration
- coldbox
- settings
- conventions
- environments
- ioc
- models
- debugger
- mailSettings
- i18n
- bugTracers
- webservices
- datasources
- layoutSettings
- layouts
- cacheEngine
- interceptorSettings
- interceptors
Available objects in variable scope
- controller
- logBoxConfig
- appMapping (auto calculated by ColdBox)
Required Methods
- configure() : The method ColdBox calls to configure the application.
Optional Methods
- detectEnvironment() : If declared the framework will call it and it must return the name of the environment you are on.
- {environment}() : The name of the environment found and called by the framework.
*/
// Configure ColdBox Application
function configure(){
// coldbox directives
coldbox = {
//Application Setup
appName = "Your app name here",
eventName = "event",
//Development Settings
debugMode = true,
debugPassword = "",
reinitPassword = "",
handlersIndexAutoReload = true,
configAutoReload = false,
//Implicit Events
defaultEvent = "General.index",
requestStartHandler = "Main.onRequestStart",
requestEndHandler = "",
applicationStartHandler = "Main.onAppInit",
applicationEndHandler = "",
sessionStartHandler = "",
sessionEndHandler = "",
missingTemplateHandler = "",
//Extension Points
UDFLibraryFile = "includes/helpers/ApplicationHelper.cfm",
coldboxExtensionsLocation = "",
pluginsExternalLocation = "",
viewsExternalLocation = "",
layoutsExternalLocation = "",
handlersExternalLocation = "",
requestContextDecorator = "",
//Error/Exception Handling
exceptionHandler = "",
onInvalidEvent = "",
customErrorTemplate = "",
//Application Aspects
handlerCaching = false,
eventCaching = false,
proxyReturnCollection = false,
flashURLPersistScope = "session"
};
// custom settings
settings = {
};
// environment settings, create a detectEnvironment() method to detect it yourself.
// create a function with the name of the environment so it can be executed if that environment is detected
// the value of the environment is a list of regex patterns to match the cgi.http_host.
environments = {
//development = "^cf8.,^railo."
};
//LogBox
logBoxConfig.appender(name="coldboxTracer",class="coldbox.system.logging.appenders.ColdboxTracerAppender");
logBoxConfig.root(levelMax=logBoxConfig.logLevels.INFO,appenders="*");
logBoxConfig.info("coldbox.system");
//Layout Settings
layoutSettings = {
defaultLayout = "Layout.Main.cfm",
defaultView = ""
};
//cacheEngine
cacheEngine = {
objectDefaultTimeout = 60,
objectDefaultLastAccessTimeout = 30,
reapFrequency = 1,
freeMemoryPercentageThreshold = 0,
useLastAccessTimeouts = true,
evictionPolicy = "LRU",
evictCount = 1,
maxObjects = 100
};
//Interceptor Settings
interceptorSettings = {
throwOnInvalidStates = false,
customInterceptors = ""
};
//Register interceptors as an array, we need order
interceptors = [
//Autowire
{class="coldbox.system.interceptors.Autowire",
properties={}
},
//SES
{class="coldbox.system.interceptors.SES",
properties={}
}
];
/*
//Register Layouts
layouts = {
login = {
file = "Layout.tester.cfm",
views = "vwLogin,test",
folders = "tags,pdf/single"
}
};
//Model Integration
models = {
objectCaching = true,
definitionFile = "config/modelMappings.cfm",
externalLocation = "coldbox.testing.testmodel",
SetterInjection = false,
DICompleteUDF = "onDIComplete",
StopRecursion = "",
parentFactory = {
framework = "coldspring",
definitionFile = "config/parent.xml.cfm"
}
};
//Conventions
conventions = {
handlersLocation = "handlers",
pluginsLocation = "plugins",
viewsLocation = "views",
layoutsLocation = "layouts",
modelsLocation = "model",
eventAction = "index"
};
//IOC Integration
ioc = {
framework = "lightwire",
reload = true,
objectCaching = false,
definitionFile = "config/coldspring.xml.cfm",
parentFactory = {
framework = "coldspring",
definitionFile = "config/parent.xml.cfm"
}
};
//Debugger Settings
debugger = {
enableDumpVar = false,
persistentRequestProfilers = true,
maxPersistentRequestProfilers = 10,
maxRCPanelQueryRows = 50,
//Panels
showTracerPanel = true,
expandedTracerPanel = true,
showInfoPanel = true,
expandedInfoPanel = true,
showCachePanel = true,
expandedCachePanel = true,
showRCPanel = true,
expandedRCPanel = true
};
//Mailsettings
mailSettings = {
server = "",
username = "",
password = "",
port = 25
};
//i18n & Localization
i18n = {
defaultResourceBundle = "includes/main",
defaultLocale = "en_US",
localeStorage = "session",
unknownTranslation = "**NOT FOUND**"
};
//bug tracers
bugTracers = {
enabled = false,
bugEmails = "",
mailFrom = "",
customEmailBugReport = ""
};
//webservices
webservices = {
testWS = "http://www.test.com/test.cfc?wsdl",
AnotherTestWS = "http://www.coldbox.org/distribution/updatews.cfc?wsdl"
};
//Datasources
datasources = {
mysite = {name="mySite", dbType="mysql", username="root", password="pass"},
blog_dsn = {name="myBlog", dbType="oracle", username="root", password="pass"}
};
*/
}
You can also check out the all new application templates which have the new CFC configuration object.
Localization & Internationalization Major Updates
Our localization, i18n and resource bundle plugins have been majorly overhauled for improvements, thread concurrency, optimizations and fixes. Paul Hastings' contributions have really made a difference and we have improved them tremendously for this release. You can also now request resources from the bundles and even pass defaults and specific locales instead of looking at the user's locale: #getResource("btn_info","NOT FOUND","en_US")#
New Handler Before and After Action Advices: pre{Action} and post{Action}
Thanks to Kalen Gibbons and our wild discussions, we found use cases to create this functionality. You can now create before and after advises (preHandler,postHandler) but on an action level. So if you are about to request an action called "index" then the following will apply:
- Execute preHandler() if it exists
- Execute preIndex() if it exists
- Execute index()
- Execute postIndex() if it exists
- Execute postHandler() if it exists
So now you can create two new method signatures according to the requested action and ColdBox will execute them for you.
Autowire Compatibility Warning
- The default DSL marker for autowiring using constructor arguments and setter methods used to be called "_wireme", which you can programmatically change to whatever you want by setting the setting: "beanFactory_dslMarker". However, now for consistency purposes the default injection name is "inject". This is now consistent framework wide. If you need something injected, then all you need to say is "inject" or "inject={DSL}".
ColdBox Debugger
- URL discovery fixes when using SES and non-SES url links, css and jss minifications, html updates, and new cool buttons.
- You can also now see in your debugger the current route that was detected and the current routed URL
- Private request collection can now be tracked and viewed
- Outputs of collection tracking are now limited to 5000 chars
onInvalidEvent Reworking and Consistency
If you use onInvalidEvent, you already know that it intercepts whenever an event is invalid. This means that the handler requested does not exist or the action requested does not exist. In pre M4, they both reacted differently and there was no consistency on the two exceptions. They now do. So whenever a handler or action is invalid, M4 will set the "invalidevent" in the private request collection, log it and override the event so you can now execute your onInvalidEvent.
New Interception Points:
You can now intercept on the following new points:
- onReinit : Whenever the application is reinitialized you can listen to it
- applicationEnd : Whenever the application is finalized via onApplicationEnd() in Application.cfc you can listen to it
- preViewRender : You can now listen when ANY view is about to be rendered. You also get as data all the arguments of the rendering.
- postViewRender : You can now listen when ANY view has been rendered in the Renderer plugin but NOT yet sent for output. You get all the arguments of the rendering plus the rendering content. You can alter the view if necessary, do caching, or whatever you like.
New Global Event Handlers:
- missingTemplateHandler : So you can intercept ColdFusion 8's onMissingTemplate(). This handler event is called whenever the missing template is detected. You receive the missing template in the request collection.
- applicationEndHandler : You can now execute an event whenever the application ends.
Request Context Updates
- Fixes on validating if a value is in the private request collection
- New method: event.getCurrentRoutedURL() which gives you the routed URL via SES
- All of a request's metadata like currentView, currentLayout, etc are now stored in the private request collection (COMPATIBILITY)
- You can now also pass in a "cacheSuffix" to setView() operations. This allows you to modify the cache key when rendering views. Incredibly useful for multi-domain applications.
Rendering Updates
- The Renderer has been optimized and it has significant rendering improvements in speed.
- The renderLayout method now can take also a view argument: renderLayout(layout,view). This basically funnels the view argument so you can use it in your layouts for a la carte renderings: <cfoutput>#renderView(arguments.view)#</cfoutput>
- You can now also pass in a "cacheSuffix" to renderView() operations. This allows you to modify the cache key when rendering views. Incredibly useful for multi-domain applications.
CF9 ORM
- We have included a cool CF9 ORM sample application called TaskManager that even uses our new ORM event handlers
- New CF9 ORM Event Handler. You can build a CF9 ORM event handler that extends our core event handler: coldbox.system.orm.hibernate.EventHandler that can give you a bridge into your ColdBox application (as long as it exists within your app). You can then bridge into the ColdBox app and request for anything you like. But not only that, you can annotate your event handler with some metadata and make it behave even more awesome. How awesome? Entity Injector Awesome! Yes, your Event Handler can be activated so it can autowire your entities with dependencies by using our awesome ColdBox DSL and BlenderBox dependency injection framework.
EventHandler Metadata To activate Entity Injector
- @injector : Boolean [false] You must set it to true to activate the injector on PostLoad()
- @injectorSetterInjection : Boolean[false] You must set it to true to let BlenderBox autowire it by setter injection alongside cfproperty injections
- @injectorStopRecursion : String[] A list of classes of when to stop recursion for looking for dependencies
- @injectorInclude : String[] A list of entity names to include in the injecting process, if entity is not found here then it is not injected
- @injectorExclude : String[] A list of entity names to exclude from the injection processes.
You can find a demo of this functionality in the TaskManager sample application or in the wiki docs: http://wiki.coldbox.org/wiki/Extras:CFORM.cfm So these are the major updates to this Milestone release. Enjoy it and continue to support ColdBox as 3.0.0 is revolutionizing!
Add Your Comment