Is it not possible to set some application variabl...
# fw1
s
Is it not possible to set some application variables from a property injected dependency inside of setupApplication()? This does not throw an error, but I am experiencing strange behavior; namely the application variables are not defined.
Copy code
property config;

...

	public void function setupApplication() {
		settings = config.getSettings();
	    	
		application.AESKey = settings.keys.AES ;
		application.captchaKey = settings.keys.captcha;
		application.VTID = settings.application.vtid;
		application.COTID = settings.application.cotid;
		application.securelist = settings.securelist;
		application.env = getEnvironment();
		
	 }
Update. actually I am getting an error.
Copy code
variable [CONFIG] doesn't exist
w
i don't believe you can inject beans into application.cfc the way you are expecting based on the above code
why not just inject config into those components where you need it? it'll be persisting in the application scope (by default) so i don't see much reason to map its values to application scoped vars, it's kind of a lateral move
s
I am doing that already and it works in setupRequest() just not in setupApplication
w
can you show your setupRequest() code? i'm not following
s
here is an example in the before() method where I am using injection:
Copy code
function before( struct rc = {} ) {
		
		
		// reset the application scope
		if(structKeyExists(rc, 'resetApp')) {
			userService.logout();
		}
			
		// user session data
		rc["userSession"] = userService.getUserSession();
				
	}
top of application.cfc has the property defined like in any other cfc:
Copy code
property config;
	property userService;
		

	// FW/1 settings
	variables.framework = {
		// action = 'action',
		// defaultSection = 'main',
		// defaultItem = 'default',
		generateSES = true,

....
w
and you're saying you have a property in application.cfc like so:
property userService;
?
weird, never tried that in app.cfc
s
yes, been doing that for a while
looks like the beanfactory may not be done wiring in setupApplication()
w
in your setupApplication() before your code above, add a
writedump( getBeanFactory().getBeanInfo() )
and see if it sees the beans, it may be too early in the lifecycle
s
ah good tip. will try
w
i'm still trying to think through injecting beans in app.cfc though, it feels wrong (whether or not it's possible). what led you to do that as opposed to explicitly calling
getBeanFactory().getBean("userService")
? it's not clear to me if i missed the boat on something
s
I just read the docs somewhere where I think I saw that app.cfc is factory aware becuase it inherits from FW and can be treated like any other controller cfc
w
sure. i've just never thought about composing app.cfc with injectables like that
s
I refactored one day and saw that a before() method was ideal for doing/checking something before every call to all my controller methods and moved code into it
hmmm.. I have done that for several projects over the years
w
sure, but as i said above, i/we've always gone about it by simply asking the beanfactory for the reference rather than injecting it. will ponder
i take it that means that you have accessors="true" in app.cfc?
s
but. I get no output dumping out getBeanInfo() just blazes past and errors on application variable not defined
yes I do have that set to true
w
throw an abort after it
s
I did
w
been a long while since i've looked at one.cfc code, but i've not encountered what you are
so not sure when beanFactory is actually available
s
It is wierd.. this aborts and shows the cfc definition
Copy code
writedump(var="#getBeanFactory()#",  abort="true");
This shows nothing
Copy code
writedump(var="#getBeanFactory().getBeanInfo('config')#",  abort="true");
w
backing up just a bit, just curious why you're setting application vars from a managed bean, must you create those?
s
actually no
w
did you do the getBeanInfo() with no args?
s
I could do as you suggest and just inject into the controllers and services directly
w
it should enumerate all managed objects
s
was looking for a way to quickly be backwards compatiablt without changing too much code
do not need to maintain the application scope vars really
w
fwiw, we typically have a _controller.cfc, _service.cfc, etc. that other cfcs extend, so you can define a single before() that all controllers inherit, if that's more appealing than doing it explicitly in every controller
s
Those application variables were hardcoded. I thought it nice to have them be set in a config file and then move them out of application.cfc.. not a big deal really
thanks. will have a look at that. It might be as the application grows
w
if you want existing application vars to feel more fw/1ish, you can do something like:
getBeanFactory().declare("config").asValue( application.config );
in setupApplication(). then it will appear to be a bean and you can inject config as a normal bean. not trying to sound remedial, i just don't know how far you may have explored
s
Thanks
s
The whole point of using DI is to avoid application variables altogether, TBH.
As noted above, setupApplication() is called very early in the application startup process so the bean factory isn't going to be initialized at that point (as you've discovered).
Instead of application variables, you should have a bean that has those properties (which you can initialize in its constructor or you could declare them in the IOC listener (you should not be calling
getBeanFactory().declare(...)
!).
And then declare that bean as a
property
in other CFCs as needed to cause it to be injected.
application
variables are just "bad".
See https://framework-one.github.io/documentation/4.3/using-di-one/#using-load-listeners for how to have a listener run when the bean factory is created and how to use a listener to declare anything custom that you need.
s
@seancorfield Thanks! I usually follow this architecture. for some reason I wound up with some application/site wide "setting" variables where I thought a bean was not necessary or appropriate and began down the path of creating an ever growing list of application variables to maintain them. I am moving them out to a bean as you suggest and will read about load listeners.
I just completed reading the use load listeners section. Am to understand that this is more of a best practice approach to loading custom settings (things like mailserver host for cfmail executions for example) than just creating a bean with a struct of values (settings) that I can inject into any controller/model cfcs using the property syntax? This seems like it might be more verbose and less self documenting than the latter approach I describe, but I might be missing something?
Copy code
function onLoad( beanFactory ) {
        beanFactory.declare( ... ).asValue( ... ).done()
            .declare( ... ).asValue( ... ).done()
            .declare( ... ).instanceOf( ... ).done()
            ...done()
            // eagerly load all the singletons:
            .load();
    }
s
I use it for multiple reasons: 1) I like to eagerly load singletons at startup rather then relying on lazy loading (safer for high-traffic to avoid potential race conditions, although I believe Steven is addressing that in the new release by adding locks? I still think eager loading is the better solution) 2) I like to be able to provide "constants" this way as constructor arguments to beans (e.g., we have a
mimeTypes
bean that we initialize in
onLoad()
with the list of mime types our app actually accepts 3) It's useful for providing aliases, renaming, overriding, and generally customizing the bean setup so that you can have both naturally-named CFCs and naturally-named properties for cases where they don't necessarily align exactly
Here's our
onLoad()
at work -- it's dramatically simpler than it used to be as we've rewritten apps from CFML to Clojure over the years but it still illustrates some of the above:
Copy code
function onLoad( di1 ) {
        var stdout = createObject( "java", "java.lang.System" ).out;
        if ( !di1.containsBean( "cfmljure" ) ) {
            if ( structKeyExists( server, "__cfmljure" ) ) {
                var cfmljure = server.__cfmljure;
                stdout.println( "DI/1 Load Listener using cached cfmljure" );
            } else {
                stdout.println( "DI/1 Load Listener creating cfmljure" );
                var cfmljure = new ws.framework.cfmljure(
                    project = "/repository",
                    boot = "/repository/build/bin/boot worldsingles"
                );
            }
            di1.addBean( "cfmljure", cfmljure );
            server.__cfmljure = cfmljure;
        } else {
            stdout.println("DI/1 Load Listener using existing cfmljure" );
        }
        // set up all the ColdSpring-specific tweaks
        // validations first:
        di1.addBean( "rules", [ ] ); // default version
        // various aliases and special case overrides (due to poor naming and conflicts):
        di1.addAlias( "clj", "ClojureService" );
        di1.addAlias( "clojure", "ClojureService" );
        // exclude beans we don't want managed/loaded by DI/1 by changing them to
        // be transients (which will exclude them from non-lazy-loading):
        var beanInfo = di1.getBeanInfo().beanInfo;
        for ( var beanName in beanInfo ) {
            var info = beanInfo[ beanName ];
            if ( info.keyExists( "CFC" ) &&
                 info.cfc.startsWith( "ws.model.orm.Bean" ) ) {
                di1.declareBean( beanName, info.cfc, false );
            }
        }
        if ( url.keyExists( "dumpbeans" ) ) { writeDump(di1.getBeanInfo()); abort; }
        // avoid lazy-loading in apps, but not in tests:
        var cfg = di1.getConfig();
        if ( !cfg.keyExists( "lazy" ) || !cfg.lazy ) {
            di1.load();
        }
    }
(we migrated from ColdSpring to DI/1 back in maybe 2012/13 so naming conventions changed quite a bit at that point)
For our TestBox tests, where we create the bean factory in
setup()
methods, we specify
lazy : true
so tests do not load the entire factory full of beans, only the ones they actually need during the test -- so tests run faster and the likelihood of race conditions there is very low.
s
Thanks. I got some things to consider now. I did notice the added benefit of lazy loading in the docs!
c
I thought that the settings component was a singleton. I don’t see why you are converting an application variable into an application variable?
Copy code
application.AESKey = settings.keys.AES ;
You can either inject the config component, directly, into your
controllers/services
or just create:
Copy code
variables.framework.environments = {
    dev = {
        someSetting = “”,
        …
    }
}
Although, I am not 100% sure you can add custom settings to
variables.framework.environments