[ColdBox 3.8.1] Lazy-Loading Aspects

I am encountering an issue which seems to indicate that aspects are instantiated immediately when they are mapped. In other words aspects are apparently never lazy-loaded. Can someone confirm that this in the intended behavior? I don’t see anything in the WireBox AOP documentation about lazy or eager loading. I can’t off the top of my head think of a reason why it would be necessary to instantiate the aspects before they are needed.

I know that the aspect is being eager loading because as part of my debugging I removed all places where the aspect was being bound to any other components. So the aspect was being mapped but not used anywhere. This seems like a prime example of when lazy-loading should be used.

The problem I am having is that I have an aspect with a dependency which has another dependency that I am trying to bind a different aspect to via a module’s configuration file. Because the aspect is being instantiated immediately so are the dependencies and then my later binding doesn’t work.

This issue took me forever to track down. It seems like WireBox should throw and exception if you attempt to bind an aspect to a singleton which has already been instantiated. An exception like that would have shaved hours off of my debugging time, instead the issue just silently failed. I could see the aspect mapping when I dumped WireBox but there was no indication that the singleton I was binding to was already initialized.

I see the following in the ColdBox code:

`

// map eagerly
map(arguments.aspect).asEagerInit().asSingleton();

`

So the decision was explicitly made to always eager init aspects but there is no indication as to why this was necessary. I found this commit (updates and more updates · ColdBox/coldbox-platform@b6a87db · GitHub) but it is a large commit and the message is not very detailed. It looks like maybe this commit introduced the mapAspect method and it has always forced eager init on them. From what I can see about the way the aspects work this doesn’t seem like it is necessary.

Good questions AJ. I’m not sure the answer. You could try removing that and see what happens :slight_smile: I’ll let Luis comment on why he did that.

As far as WireBox knowing if an aspect is mapped AFTER the creation of an object it would have been bound to-- I don’t know how that work. WireBox doesn’t keep track of the objects it creates.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Thanks for your response Brad.

If WireBox doesn’t keep track of the objects it creates how does it know not to create another instance of an object that is supposed to be a singleton when an instance already exists?

It can ask a scope if it already contains an object, but as a general rule, it doesn’t keep track. Transients, for instance would be impossible to keep track of. AOP applies to everything WireBox creates, not just singletons and AOP doesn’t behave any differently for singletons than for any other scoped (or no-scoped) object. There are also cachebox scopes, request scopes, application scopes, and even user-defined custom scopes.

AOP is just a simple WireBox listener. When an object is created, it gets a chance to influence that object. After that, it’s off into the ether.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Right, but during the AOP processing it should be able to access the Mapping object for the object that it is about to decorate. At that point it could do a check if it is a singleton and if it were able to perform that same “do you have an instance” check it could then throw and exception. Though I am not familiar enough with that code to know if that check is easily accessible to the AOP processing code. But it is all part of WireBox right, so even it took a little work to get at that check it would still all be contained within the WireBox module.

That scenario would never exist since WireBox would never create a second singleton in the first place, thus the AOP would never be fired twice for the same mapping. Is that not what you’re seeing?

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Right, the issue isn’t that ColdBox is creating another singleton. The issue is that when I map an aspect in the base application it is creating the singletons which are dependencies of that aspect. Then later in the module I cannot map any aspects to those singletons because they already exists. It makes sense that I can’t map the additional aspect because the singleton exists, but my point is that there is no exception or warning that is the case and it just fails silently.

Right now I am following all of the module examples I have seen and I am using the “binder” variable in the “configure” method in my “ModuleConfig.cfc”. Is there a better place to define IOC mappings in modules? Someplace that would execute before the AOP mappings in the base application are processed.

BTW. I removed the “.asEagerInit()” from Binding.cfc and not only is my issue fixed by I have not encountered any issues in our application as a result. I am going to make this update to our fork and depending on what Luis has to say I may submit a pull request.

Perhaps a possible solution would be to default to eager init in that method but add a new argument to do lazy init. This would ensure there would be no unintended consequences for existing applications.

Modules don’t load at all until the parent app configuration is loaded.

Can you try wrapping the singletons in a provider? Their creation will then at least be deferred to their first use.

property name=“mySingleton” inject=“provider:mySingleton”;

there is no exception or warning that is the case and it just fails silently.

Again, I don’t think there should be, or really can be one. I understand the current out-of-order sorts are not desired by you, but there’s nothing to say someone else may purposefully map and unmap aspects at will during the life of an application, fully desiring them to only apply to objects that were created while the aspect was mapped. Your needs are specific to you, and not really assumptions that the framework would be safe (or performant) in making.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

That’s good to hear. You can also put in a ticket if you like. The conversation might be less likely to get lost in JIRA.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

I am not unmapping any aspects and everything I am doing is during the configuration portion of the application lifecycle. Mapping an aspect inside of a modules seems like a legitimate thing to do, I haven’t seen any evidence to the contrary. Nor have I see anyone suggest that having an aspect pull in dependancies is an unusual thing to do. I feel like a may not be clearly convey what it is that my application is doing.

I understand what your application is doing, what I’m trying to tell you is that you’re suggestion that any time Wirebox maps an aspect, it should stop and check all the previous objects it’s ever created (that it can find), determine if any of those CFCs and/or method combinations match the binding on said aspect, and throw an error if they do. That suggestion might make sense to you in your very specific situation, but doesn’t make sense in WireBox in general. My example of unmapping things was just an example of other perfectly valid things developers can do with WireBox and we need to consider all those use cases.

Outside of aspects not being eager initted, or you switch them to use providers, I don’t know what to tell you. Application startup is a complicated and tricky process with many chicken-and-egg scenarios. Unfortunately this is one of them. No matter what order we load things in, somebody somewhere will have an application that wanted it in a different order. We have the same sort of issue right now with putting logging appenders in modules-- you can’t actually use them in your parent config yet because they don’t exist yet. There are no good answers for that right now but we’re open to suggestions.

Actually, I just thought of another suggestion-- if you have stuff that can’t load until AFTER all modules, configs, loggers, aspects, models, etc, etc, etc have been loaded, try putting it in an afterConfigurationLoad interceptor. That doesn’t fire until all the modules are done loading up.

http://wiki.coldbox.org/imagelibrary/ColdBoxLifecycles.jpg

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com