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...
Interceptors are CFCs that extend the ColdBox Interceptor class (coldbox.system.Interceptor
), implement a configuration method called configure()
, and then contain methods for the events it will listen for. All interceptors are treated as singletons in your application, so make sure they are thread safe and var scoped.
Info You can also remove the inheritance from the CFC (preferred method) and WireBox will extend the
coldbox.system.Interceptor
for you using Virtual Inheritance.
You can use CommandBox to create interceptors as well:
Interceptors can be registered in your Coldbox.cfc
configuration file using the interceptors
struct, or they can be registered manually via the system's interceptor service.
In ColdBox.cfc
:
Registered manually:
configure()
MethodThe interceptor has one important method that you can use for configuration called configure()
. This method will be called right after the interceptor gets created and injected with your interceptor properties.
Info These properties are local to the interceptor only!
As you can see on the diagram, the interceptor class is part of the ColdBox framework super type family, and thus inheriting the functionality of the framework.
There are many interception points that occur during an MVC and Remote life cycle in a ColdBox application. We highly encourage you to follow our flow diagrams in our ColdBox Overview section so you can see where in the life cycle these events fire. Also remember that each event can pass a data structure that can be used in your application.
You can also register CFCs as interceptors programmatically by talking to the application's Interceptor Service that lives inside the main ColdBox controller. You can access this service like so:
Once you have a handle on the interceptor service you can use the following methods to register interceptors:
registerInterceptor()
- Register an instance or CFC path and discover all events it listens to by conventions
registerInterceptionPoint()
- Register an instance in a specific event only
Here are the method signatures:
Examples
By convention, any interceptor CFC must create a method with the same name as the event they want to listen to. This method has a return type of boolean
and receives 3 arguments. So let's explore their rules.
event
which is the request context object
interceptData
which is a structure of data that the broadcaster sends
buffer
which is a request buffer object you can use to elegantly produce content that is outputted to the user's screen
rc
reference to the request collection struct
prc
reference to the private request collection struct
The intercepting method returns boolean
or void
. If boolean then it means something:
True means break the chain of execution, so no other interceptors in the chain will fire.
False or void
continue execution
Also remember that all interceptors are created by WireBox, so you can use dependency injection, configuration binder's, and even AOP on interceptor objects. Here is a more complex sample:
HTTP Security Example:
Interceptors can be declared in the Coldbox.cfc
configuration file or programmatically at runtime. If you register them in the configuration file, then you have control over the order in which they fire. If you register interceptors programmatically, you won't have control over the order of execution.
In the configuration file, interceptors are declared as an array of structures in an element called interceptors
. The elements of each interceptor structure are:
class
- The required instantiation path of the CFC.
name
- An optional unique name for the interceptor. If this is not passed then the name of the CFC will be used. We highly encourage the use of a name to avoid collisions.
properties
- A structure of configuration properties to be passed to the interceptor
In ColdBox.cfc
:
Example
Info Remember that order is important!
Interceptors are CFC listeners that react on incoming events. Events can be announced by the core framework or be custom events from your application. These interceptors can also be stacked to form interceptor chains that can be executed implicitly for you. This is a powerful feature that can help developers and framework contributors share and interact with their work. (Read more on Intercepting Filters)
The way that interceptors are used is usually referred to as event-driven programming, which can be very familiar if you are already doing any JavaScript or AngularJS coding. You can listen and execute intercepting points anywhere you like in your application, you can even produce content whenever you announce:
Custom Announcements
If you are familiar with design patterns, custom interceptors can give you an implementation of observer/observable listener objects, much like any event-driven system can provide you. In a nutshell, an observer is an object that is registered to listen for certain types of events, let's say as an example onError
is a custom interception point and we create a CFC that has this onError
method. Whenever in your application you announce or broadcast that an event of type onError occurred, this CFC will be called by the ColdBox interceptor service.
Interceptor Example
Below are just a few applications of ColdBox Interceptors:
Security
Event based Security
Method Tracing
AOP Interceptions
Publisher/Consumer operations
Implicit chain of events
Content Appending or Pre-Pending
View Manipulations
Custom SES support
Cache advices on insert and remove
Much much more...
*
You can restrict the execution of an interception point by using the eventpattern
annotation. This annotation will be placed in the function and the value is a regular expression that the interceptor service uses to match against the incoming event. If the regex matches, the interception function executes, else it skips it. This is a great way for you to create interceptors that only fire not only for the specific interception point you want, but also on the specific incoming event.
also announces several other events in the object creation life cycles, so please see
Our caching engine, , also announces several events during its life cycle, so please see The as well.
Interception Point | Intercept Structure | Description |
preLayout | --- | This occurs before any rendering or layout is executed |
preRender | {renderedContent} | This occurs after the layout+view is rendered and this event receives the produced content |
postRender | --- | This occurs after the content has been rendered to the buffer output |
preViewRender | view - The name of the view to render | This occurs before any view is rendered |
postViewRender | All of the data above plus: | This occurs after any view is rendered and passed the produced content |
preLayoutRender | layout - The name of the layout to render | This occurs before any layout is rendered |
postLayoutRender | Everything above plus: | This occurs after any layout is rendered and passed the produced content |
Interception Point | Intercept Structure | Description |
afterConfigurationLoad | --- | This occurs after the framework loads and your applications' configuration file is read and loaded. An important note here is that your application aspects have not been configured yet and no modules have been loaded. |
afterAspectsLoad | --- | This occurs after the configuration loads and the aspects have been configured. This is a great way to intercept on application start after all modules have loaded. |
preReinit | --- | This occurs every time the framework is re-initialized |
onException | exception - The cfcatch exception structure | This occurs whenever an exception occurs in the system. This event also produces data you can use to respond to it. |
onRequestCapture | --- | This occurs once the FORM/URL variables are merged into the request collection but before any event caching or processing is done in your application. This is a great event to use for incorporating variables into the request collections, altering event caching with custom variables, etc. |
onInvalidEvent | invalidEvent - The invalid event | This occurs once an invalid event is detected in the application. This can be a missing handler or action. This is a great interceptor to use to alter behavior when events do not exist or to produce 404 pages. |
applicationEnd | --- | This occurs when the application is stopped, typically on running |
sessionStart | session structure | This occurs when a user's session starts |
sessionEnd | sessionReference - A reference to the session structure | This occurs when a user's session ends |
preProcess | --- | This occurs after a request is received and made ready for processing. This simulates an on request start interception point. Please note that this interception point occurs before the request start handler. |
preEvent | processedEvent - The event that is about to be executed | This occurs before ANY event execution, whether it is the current event or called via the run event method. It is important to note that this interception point occurs before the preHandler convention. (See Event Handler Guide Chapter) |
postEvent | processedEvent - The event that is about to be executed | This occurs after ANY event execution, whether it is the current event or called via the run event method. It is important to note that this interception point occurs after the postHandler convention (See Event Handler Chapter) |
postProcess | --- | This occurs after rendering, usually the last step in an execution. This simulates an on request end interception point. |
preProxyResults | {proxyResults} | This occurs right before any ColdBox proxy calls are returned. This is the last chance to modify results before they are returned. |
Interception Point | Intercept Structure | Description |
preModuleLoad | {moduleLocation, moduleName} | This occurs before any module is loaded in the system |
postModuleLoad | {moduleLocation, moduleName, moduleConfig} | This occurs after a module has been loaded in the system |
preModuleUnload | {moduleName} | This occurs before a module is unloaded from the system |
postModuleUnload | {moduleName} | This occurs after a module has been unloaded from the system |
The last piece of the puzzle is how to announce events. You will do so via the inherited super type method announceInterception()
that all your handlers,plugins and even the interceptors themselves have or via the interceptor service processState()
method. This method accepts an incoming data struct which will be broadcasted alongside your event:
Hint Announcing events can also get some asynchronous love, read the Interceptor Asynchronicity for some asynchronous love.
Once your custom interception or event points are registered and CFC are registered then you can write the methods for listening to those events just like any other interceptor event:
Every interception point receives a unique request output buffer that can be used to elegantly produce output. Once the interception point is executed, the interceptor service will check to see if the output buffer has content, if it does it will advice to write the output to the ColdFusion output stream. This way, you can produce output very cleanly from your interception points, without adding any messy-encapsulation breaking output=true
tags to your interceptors. (BAD PRACTICE). This is an elegant solution that can work for both core and custom interception points.
Hint Each execution point will have its own clean buffer to work with. As each interception point has finalized execution, the output buffer is flushed, only if content exists.
Here are some examples using the buffer
argument:
Here are some common methods on the buffer object:
append( str )
Append strings to the buffer
clear()
Clear the entire buffer
length()
Get size of the buffer
getString()
Get the entire string in the buffer
getBufferObject()
Get the actual java String Builder object
You can use the interceptor service to register new events or interception points via the appendInterceptionPoints()
method. This way you can dynamically register events in your system:
The value of the customPoints
argument can be a list or an array of interception points to register so the interceptor service can manage them for you:
In addition to all the events announced by the framework, you can also register your own custom events.
Curt Gratz shares a brief example and use case for ColdBox custom interception points in his video ColdBox Custom Interception Points - How To.
In the ColdBox.cfc
configuration file, there is a structure called interceptorSettings
with two keys:
throwOnInvalidStates
- This tells the interceptor service to throw an exception if the state announced for interception is not valid or does not exist. By default it does not throw an exception but ignore the announcement.
customInterceptionPoints
- This key is a comma delimited list of custom interception points you will be registering for execution.
The customInterceptionPoints
is what interest us. This can be a list or an array of events your system can broadcast. This way, whenever interceptors are registered, they will be inspected for those events.
Interception Point | Intercept Structure | Description |
afterHandlerCreation | handlerPath - The handler path | This occurs whenever a handler is created |
afterInstanceCreation | mapping - The object mapping | This occurs whenever a object is created |
There are several reporting and utility methods in the interceptor service that I recommend you explore. Below are some sample methods:
The third use case is exactly the same scenario as the async listeners but with the exception that the caller will not wait for the spawned threads at all. This is accomplished by using the asyncAllJoin=false
flag. This tells ColdBox to just spawn, return back a structure of thread information and continue execution of the calling code.
You can also combine this call with the following arguments:
asyncPriority
: The priority level of each of the spawned threads. By default it uses normal
priority level
The second use case is where you want to run the interception but multi-thread all the interceptor CFCs that are listening to that interception point. So let's say we have our onPageCreate
announcement and we have 3 interceptors that are listening to that interception point. Then by using the asyncAll=true
argument, ColdBox will create 3 separate threads, one for each of those interceptors and execute their methods in their appropriate threads. This is a great way to delegate long running processes simultaneously on a specific piece of data. Also, by default, the caller will wait for all of those 3 threads to finalize before continuing execution. So it is a great way to process data and wait until it has become available.
Caution Please remember that you are also sharing state between interceptors via the
event
andinterceptData
, so make sure you either lock or are careful in asynchronous land.
You can also combine this call with the following arguments:
asyncPriority
: The priority level of each of the spawned threads. By default it uses normal
priority level
asyncJoinTimeout
: The timeout in milliseconds to wait for all spawned threads. By default it is set to 0, which waits until ALL threads finalize no matter how long they take.
asyncAllJoin
: The flag that determines if the caller should wait for all spawned threads or should just spawn all threads and continue immediately. By default, it waits until all spawned threads finalize.
We have also extended the interceptor registration process so you can annotate interception points to denote threading. You will do so with the following two annotations:
This allows you the flexibility to determine in code which points are threaded, which is a great way to use for emails, logging, etc.
So if you have 3 interceptors in a chain and only 1 of them is threaded, then the 2 will execute in order while the third one in the background. Overall, your processing power and choices have now grown.
Each interceptor can unregister itself from any event by using the unregister( state )
method. This method is found in all Interceptors or can be accessed via the interceptor service as well.
The first case involves where you want to completely detach an interception call into the background. You will accomplish this by using the async=true
argument. This will then detach the execution of the interception into a separate thread and return to you a structure containing information about the thread it created for you. This is a great way to send work jobs, emails, processing and more to the background instead of having the calling thread wait for execution.
You can also combine this call with the following arguments:
asyncPriority
: The priority level of the detached thread. By default it uses normal
priority level.
Argument
Type
Required
Default Value
Description
async
none
false
false
If the annotation exists, then ColdBox will execute the interception point in a separate thread only if not in a thread already.
asyncPriority
string : low,normal,high
false
normal
The thread priority that will be sent to each cfthread call that is made by the system.
In honor of one of my favorite bands and album, The Police - Synchronicity, we have some asynchronous capabilities in ColdBox Interceptors. These features are thanks to the sponsorship of Guardly, Inc, Alert, Connect, Stay Safe. So please make sure to check them out and thank them for sponsoring this great feature set. The core interceptor service and announcement methods have some arguments that can turn asynchronicity on or off and can return a structure of threading data.
The asynchronous arguments are listed in the table below:
All asynchronous calls will return a structure of thread information back to you when using the announceInterception()
method or directly in the interceptor service, the processState()
method. The structure contains information about all the threads that where created during the call and their respective information like: status, data, errors, monitoring, etc.
Now that you have seen all asynchronous arguments and their features, let's investigate each use case one by one.
Argument
Type
Required
Default Value
Description
async
boolean
false
false
Threads the interception call so the entire execution chain is ran in a separate thread. Extra arguments that can be used: asyncPriority.
asyncAll
boolean
false
false
Mutually exclusive with the async argument. If true, this will execute the interception point but multi-thread each of the CFCs that are listening to the interception point. So if 3 CFCs are listening, then 3 separate threads will be created for each CFC call. By default, the calling thread will wait for all 3 separate threads to finalize in order to continue with the execution. Extra arguments that can be used: asyncAllJoin, asyncTimeout, asyncPriority.
asyncAllJoin
boolean
false
true
This flag is used only when combined with the asyncAll argument. If true (default), the calling thread will wait for all intercepted CFC calls to execute. If false, then the calling thread will not join the multi-threaded interception and continue immediate execution while the CFC's continue to execute in the background.
asyncPriority
string : low,normal,high
false
normal
The thread priority that will be sent to each cfthread call that is made by the system.
asyncJoinTimeout
numeric
false
0
This argument is only used when using the asyncAll and asyncAllJoin=true arguments. This argument is the number of milliseconds the calling thread should wait for all the threaded CFC listeners to execute. By default it waits until all threads finalize.