RE: [coldbox:15482] Re: [coldbox-3.5.1] Injection Happy

Sorry I led you astray about interceptors not always being singletons. I forgot that can’t be changed.

I don’t know if I 100% follow the code you posted below, but thanks for providing it. As far as the specific question, “How do I inject a short-lived (ie, session) object into an eternal (ie singleton) object?”, here’s you’re answer. The trick with scope-widening injection is that if you just put a straight reference to your session-scoped object into your singleton, then not only with ALL users get whatever the first session object that was passed in (bad), but when that session expires, the hard reference in your singleton will keep it from being collected from memory which is why you say you “widened” the scope of the session object. (also bad)

Three suggestions have been given so far to avoid giving your interceptor a hard reference to a specific session-scoped object:

  1. Inject another singleton service object into your interceptor which has a method that will return the current session-scoped object
  2. Inject WireBox itself in (actually it’s already available as variables.wireBox because CB is awesome like that) and simply ask WireBox for the session-scope object when you need it
  3. Inject a WireBox provider that is essentially a dummy object that doesn’t reference anything, but it “knows” how to get it when you ask. If it is providing a session-scoped object it will always return the appropriate one when you ask it based on the current session.

All three of those options really boil down to the same principle: Inject another object with the same scope of the interceptor that can “find” the object you really need when you ask for it each time.

  1. The first option is my least favorite simply because it seems like an unnecessary service method. Of course, that could change if your service has some “extra” massaging it wants to do to the session-scoped object prior to returning it. Then it might make perfect sense.
  2. The second option is probably what I would do if I were coding this. Basically in your WireBox binder config you’d have map(“mySesionObject”).to(“path.to.object”).into(this.SCOPES.SESSION); and in your interceptor you’d simply do wireBox.getInstance(“mySessionObject”) every time you want to use it. Just never store the session-scoped object in the variables or this scope of the interceoptor-- always keep it in a locally-scope variable so it doesn’t bleed over to other people’s session.
  3. The third option is one of the extra-cool features of WireBox which was designed exactly for this sort of thing. Basically, instead of autowiring mySessionObject directly into the variables scope of the interceptor, you’d do this: property name=“mySessionProvider” inject=“provider:mySessionObject”; This will inject an instance of coldbox.system.ioc.Provider. Then you can call variables.mySessionProvider.get() any time you want the actual object. Even cooler is that the provider class will proxy any method calls via onMissingMethod so variables.mySessionProvider.sessionObjectMethod() will also seamlessly work.

Hopefully that helps a bit.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

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

Mike,

On a side note to what the others have said, I think you might be over complicating the solution.

Now from what I am reading from you, you have an object that is storing user information that is essentially a bean, and that this object is stored in the session scope. And within your interceptor you’re looking at getting this object to do further things with, yes?

Now from that observation I would be more inclined to go a simpler route, and inject the session storage plugin into the interceptor. Thus you can then retrieve the session user object with the sessionStorage,get() which would eliminate any scope widening issues as well.

Amazing explanation…exactly the kind of information I needed. Is this your own brilliant self or is this “best practice” explanation buried in docs somewhere?

Mike

Absolutely right Andrew… over complicating is how I learn. Simplification and faster, reusable code is soooooo much more satisfying once I’ve learned how to complicate the crap out of the code :slight_smile: Don’t hate me :slight_smile:

But seriously…I could sense I was…as the original post subject suggested…over indulging my injection. It’s just such a cool way to get things wired together, but I found I was missing some “part” in my understanding of both why and how…I am utterly amazed at the level of clearly competent and useful advice and recommendation and really grateful for it.

The session storage facade sounds like a good way to go…now that I have about three to four ways to accomplish the same task, I want to try them all!

whip me, beat me, make me write good code!

Mike

Mike,

I don’t think anyone on this list will judge you, because you are right, it is the only way to learn.

Question, related.

I’m not clear on this…are mapped session scope wirebox references stored in the same way to session that makes them available to sessionStorage.getVar()? I thought this would work:

component extends=“coldbox.system.Interceptor” {
property name=“hostservice” inject=“id:hostservice”;
property name=“sessionfacade” inject=“coldbox:plugin:sessionStorage”;

public void function preProcess(event, interceptData) {
var rc = event.getCollection();
var prc = event.getCollection(private=true);
var cdom = sessionfacade.getVar(“currentDomain”);
var temp = hostservice.getByHost(rc.cgiscope.gethttphost());
prc.validationResults = validateModel( temp );
if( prc.validationResults.hasErrors() ){
getPlugin(“MessageBox”).error( prc.validationResults.getAllErrors() );
}
else{
cdom.setMemento(temp.getMemento());
arguments.event.setSESBaseURL(“http://” & rc.cgiscope.gethttphost() & “/index.cfm”);
}
};
}

…and the mapping is…

map(“currentDomain”).to(“model.domain.DomainHost”).into(this.SCOPES.SESSION).asEagerInit();

…but the setMemento() line fails because only an empty string is being returned from sessionStorage. This at first tells me one of two things might be true.

  1. I don’t know what I’m doing…there are not a lot of details for this plugin, though as far as it’s use, there probably don’t need to be…how it functions or why you would use in a practical way would be helpful.
  2. If you don’t setVar() with sessionStorage, you can’t getVar() expecting any session scoped variable would be validly returned from the CB framework.
    Thoughts & wisdom?

Mike

Mike,

It would work except that session storage uses a key to store the data, that means that you would also need to store using session storage setvar().

Which btw is something I forgot to mention earlier, and because session storage uses the lock it should also be the preferred way to get and set variables in all scopes.

Now I am not sure if the method your doing at the start is really part of this now, in other words the user session object should be stored at some other stage, in this case I would be assuming when the user logs in then you could populate the object and store it in the session.

So the mapping

map(“currentDomain”).to(“model.domain.DomainHost”).into(this.SCOPES.SESSION).asEagerInit();

Would become obsolete, because this would make a snapshot at the time the mapping was created, I am assuming that is what it would do.

I prefer to stay away from the map for wirebox as much as possible, the reason behind that is I am under the assumption that too much hard coded dependencies can become a nightmare later on.

Don’t get me wrong they are very powerful, but I think there is a right and wrong time to start using them. In your case you may not even actually need this, unless the map your setting up is already a component that is being used that was written outside of ColdBox, in other words sharing code from somewhere else.

So my preferred method for anything that is being developed inside the Coldbox way is to use annotations, as it makes life easier to migrate and move code around without relying on to much other configurations.

Anyway going back to what your trying to achieve, I am guessing and correct me if I am wrong, the interceptor will be doing some pre process for whether a user is logged in or not. If this is the case passing that info into the int stage is not required in your example. Because you would have created this somewhere else, as noted above when a user logs in or out for example. If that also uses the session storage to store the object, then as suggested you would just pull the object back out again via the session storage.

Wow…excellent insight.

Quoting: " So my preferred method for anything that is being developed inside the Coldbox way is to use annotations, as it makes life easier to migrate and move code around without relying on to much other configurations." Initially I was thinking that by using wirebox to manage these maps provided a sort of self-documenting features of global variables for other developers to easily follow. Like “currentUser”, “currentHost”, “currentApp”…etc. But I have learned that since it will be more common to retrieve these values via getInstance(), the syntax become self-identifying. Not sure what I think about that now.

Annotations, while I read about it, I did not get the sense that this was going to be a good option. Not really for any good reason either…more for the following: I have alot of developers who do not know CB yet…comments get deleted all the time…that, to me anyway, is the disadvantage of annotations. Provided of course, that is what you are referring to.

Mike

Yes Mike, but think of it like this

/*
extends coldbox.system.Interceptor
singleton
*/

component {

}

or like this

component extends=“coldbox.system.Interceptor” singleton {

}