I consider FW/1 3.0 to be feature complete at this point so I am releasing Alpha 1 for testing. I expect people to run into a few bugs - this release has some big changes in it, compared to the 2.x release stream - and it’s possible that new feature requests will crop up during alpha testing, but everything I wanted to change is in place.
You can download FW/1 3.0 Alpha 1 from Github and that release page has a link to the complete list of closed tickets in FW/1 3.0 Alpha 1 although I’m going to summarize the most important ones in this blog post.
First and foremost, some features that have been part of FW/1 from the early days have been removed. These features were deprecated in FW/1 2.5 as a migration path so I would strongly advise anyone still on FW/1 2.2.1 (or earlier) to upgrade to 2.5 in preparation for the (breaking) changes in 3.0!
These features include the
service() API call and the
end*() item handlers within controllers, as well as global references to
rc (where it was not passed as an argument or made available in a view automatically). You can read more about the deprecation (and now removal) of these features in the release announcement for FW/1 2.5 on this blog. Management of services via a bean factory, with property-based injection, and direct invocation has long been considered a much better way to interact with services than using the “service queue” that FW/1 originally provided.
In addition, the recently added
getRCValue() API calls - added in FW/1 2.5 during the deprecation of global references to
rc - have been deprecated and will be removed in the final FW/1 3.0 release. They were hastily added and they were unnecessary. In this alpha release, their use will trigger an exception explaining what to use instead. You can add:
framework.enableLegacyRCAccessors = true
to your configuration while you update your code (this will suppress the exception but still write a message to your application server’s console output - just as the deprecation process did in FW/1 2.5).
Automatic Bean Factory Usage
The other big change in this 3.0 release is that DI/1 (and AOP/1) is fully integrated. FW/1 itself moves from
framework.aop (AOP/1), and some helper CFCs.
org.corfield.framework still exists but will issue a deprecation warning if it is used. It will be removed in the final 3.0 release.
You can still place the FW/1 CFCs anywhere you want but if you move DI/1, you’ll need to tell FW/1 where to find it - see below.
Previously, it was expected that you create a bean factory in your
setupApplication() function and call FW/1’s
setBeanFactory() API to tell the framework about it. For some time, he conventional path to your Model CFCs has been
/model/beans for your transient beans (domain objects) and
/model/services for your singleton services (and perhaps
/model/gateways for any data gateways, although those could just as easily live in your services folder too). That means you nearly always had the following code in
var bf = new framework.ioc( "model" ); setBeanFactory( bf );
Or, if you also managed your controllers this way, you may have had:
var bf = new framework.ioc( "model,controllers" ); bf.addBean( "fw", this ); setBeanFactory( bf );
addBean() call ensures that the bean factory knows
fw is an alias for your bean factory so it will be available to any controller
init( any fw ) methods when they are constructed.
If you use subsystems, you probably had something similar in your
setupSubsystem() function (and hopefully you set the default bean factory as a parent for each subsystem bean factory).
Now, FW/1 does this for you automatically. There are new configuration options to control the details, but the default cases should just work and you can remove your bean factory creation code from your
setupApplication() function. Those options are:
diEngine- the type of the dependency injection engine: FW/1 knows about “di1”, “aop1”, and “wirebox”. The default is “di1”. You can also specify “none” to suppress the automatic bean factory machinery and “custom” if you want to tell FW/1 to use your own bean factory (see below). Note that ColdSpring is deliberately not supported as it is no longer maintained by anyone and has not been updated in years.
diComponent- the default location of the bean factory CFC. For DI/1, this is
framework.ioc; for AOP/1, this is
framework.aop; and for WireBox, this is
framework.WireBoxAdapter. If you move these files elsewhere, or setup a different mapping for them, set
diComponentto that new location. If
diEngineis “custom”, you can set
diComponentto the dotted path of your bean factory for FW/1 to use it automatically.
diLocations- the set of folders that DI/1, AOP/1, or WireBox will scan for CFCs. The default is “model,controllers” - note the relative paths! If you have these folders elsewhere (i.e., not relative to the application root), then you’ll need to specify
diLocations, e.g., as
"/myapp/model,/myapp/controllers"or something similar.
diConfig- additional configuration passed to DI/1, AOP/1, WireBox, or your custom bean factory. Specifically, this is the second argument to the constructor for DI/1 or AOP/1, and the
propertiesargument to the constructor for WireBox, or the single argument to the constructor for your own bean factory. By default, it is an empty struct.
In addition to the two major changes listed above, there are a number of minor enhancements compared to FW/1 2.5:
isUnhandledRequest( string targetPath )- a new API that you can override to tell FW/1 not to handle certain requests. By default, this returns true for certain file extensions and certain paths, as specified by the
unhandledPathsconfiguration values but you can choose to override this completely, or still call
super.isUnhandledRequest(targetPath)and add additional conditions of your own.
redirectCustomURL( string uri, string preserve = 'none', statusCode = '302' )- a new API that uses
buildCustomURL()to construct a URL for a redirect.
buildCustomURL()- now supports variable substitution: if
:varnameis present in the URI passed in and
rc.varnameexists and is a simple value, then that value will be substituted into the returned URL. To avoid confusion with subsystem paths,
:varnamewill only be recognized if it follows one of:
setLayout()- now accepts an optional second argument, a boolean, that let’s you tell FW/1 to automatically suppress any further layouts. This removes the need to specify
request.layouts = falsein your layout file.
- Both DI/1 and FW/1 now try very hard to avoid attempting to autowire FW/1 itself (or the Application.cfc based on it, which acts as a global controller in a FW/1 application).