Using the provider namespace in ColdBox/WireBox with JavaLoader
ColdBox has a tremendously powerful IOC engine called WireBox bundled with the framework. With WireBox, you can map your entire model folder into ColdBox with a single line of code in your Wirebox.cfc configuration file:
mapDirectory( "model" );
Then, when you want to include a component from your model in a handler, an interceptor, or another component from the model, all you need to do is inject it via a property:
property name="MyComponent" inject="model";
Sometimes, though, because of the order in which ColdBox builds the application when it first launches, this technique of immediate injection is not sufficient. In this case, you can use the provider namespace to delay instantiation by providing a system.ioc.Provider component that can instantiate the component you need at runtime.
Specifically, I ran into this problem while converting a ColdBox 3.7 application to ColdBox 4.1.0. One of the big changes from 3.7 to 4.x is that several ColdBox plugins have been moved into modules, so they interact differently with the application and require some re-coding.
In my case, I was working with the JavaLoader plugin that loads Mark Mandel's JavaLoader library to enable you to dynamically load Java classes inside your CFML app. Under CB 3.7, I could just say:
property name="Javaloader" inject="coldbox:plugin:Javaloader";
Under CB 4.x, that syntax will no longer work, since Javaloader has been moved to module. Instead, you need to reference the module namespace that registers itself with Wirebox when the application initializes:
property name="javaloader" inject="loader@cbjavaloader";
However, this technique falls short when you need to inject the component that contains this property into an interceptor in ColdBox, because the interceptor will be created before the module is instantiated.
Provider namespace
The provider namespace allows you to delay instantiation of the object by returning a provider class that allows you to instantiate the object at a later time. In my case, I needed to inject a Utils component containing a reference to Java classes into an interceptor, but without the provider namespace, the component was generating an error on application start. Adding the provider namespace fixes the error:
property name="provider" inject="provider:loader@cbjavaloader";
Notice that my property is no longer called javaloader, because it isn't a Javaloader class anymore. Now it is a Provider class, which means I still need to instantiate the Javaloader class at runtime. Rather than use this injector method all over the application and instantiate lots of copies of Javaloader, I decided to bake a getJavaloader() method into my Utils class:
public any function getJavaLoader(){
if( isNull( variables.javaloader ) ){
variables.javaloader = provider.get( "loader@cbjavaloader" );
}
return variables.javaloader;
}
Then, whenever I need to load a Java class anywhere in the application, all I need to do is inject the Utils class:
property name="Utils" inject="model";
Since the Utils class is loaded into the application scope as a Singleton, when I call Utils.getJavaLoader() I will be creating a Singleton Javaloader class inside the Utils class. Whenever I need to load a Java class now, I just inject the Utils class and call:
local.javaloader = Utils.getJavaLoader();
local.PasswordHash = local.javaloader.create( "com.bonnydoonmedia.security.PasswordHash" );
Then, as you see, I can instantiate any Java class I need with minimal configuration management.
Special thanks to Brad Wood for this solution, which he helped me with on the CFML Slack channel.