If interceptors are always singletons, I think I have a problem in my thinking. If a SaaS application has many clients and those clients have customizations that override core application code, one way to accomplish this would be to register (in a database first) custom interceptors that could be loaded dynamically when the application is accessed via a particular client. In and of itself, this seems smart. But further study suggests that if, once loaded by a client, that interceptor is always going to be available until the application is reinitialized or the CF instance is restarted.
A couple thoughts / problems come up:
I know there are some pretty advanced caching with cachbox but I’m not that familiar with that and (as you read on, I am thinking this would not help much)
How do I unload an interceptor by knowing no one is accessing the application from a particular host?
If several clients (but not enough to justify making it a global cross-client interceptor) used the same or similar interceptor, they’d all still need to have unique names so that if multiples were loaded there would be no conflict.
It seems that this is almost an interceptor in the request scope but I don’t think that is possible.
Right track, wrong track, another way, don’t bother?
Are you going down the externalLocations route?
If you are, then all “customised” clients will have their own Coldbox.cfc, and therefore they’ll be unique “applications” (which just share the same code-base). In this instance, an interceptor as a singleton would not have any conflict with another singleton with the same name, as they’ll be sandboxed into their own “applications”. Adding these custom interceptors is also pretty easy, as you just add them in Coldbox.cfc.
If you didn’t have interceptors as singletons – just set cache=true cachetimeout=90 (for example), cachebox will automatically purge ones when it deems necessary, or after 90 mins.
I want to go down the route you recommended…but I have to be honest, it’s just baffling me a little bit…the concept may just be a bit more than I can chew right now since I’ve not been doing CB very long at all. The questions are stemming from my desire to get my current client to move in this direction…for frameworks, CB is the only one that seems to offer everything I think they’d need or can be extended to accomplish the same…but that does not mean I’m yet in a position where I can code the solution.
The other thing is we have pretty strict “hands off” policies on our hosting services so I’m not sure symlinks are going to be an available option for us, no matter how innocuous they might be in the grand scheme of things.
So at least it seems I might be able to manage client-specific interceptors by caching them via a timeout…that’s good to know.
Thanks Tom…you have been extraordinarily helpful getting me in the right direction.
Well, the externalLocations option is great if a.) you have direct access to the server (as you’ve mentioned), and b.) the customisations (or “unique” clients requiring customisation) is minimal; say maybe 50 - we currently run a SaaS application that has 15 customised clients, and 150 or so “standard” clients. In Apache, for instance, we have 16 virtualhosts – one wildcard VH (like a catch-all), and 15 specific VHs, which map to their own directory on the server. These directories are where the symlinks live which point to the core application. So everyone gets a custom URL .myapp.com, but some are “caught” by apache where there is a named virtualhost. So when we add a “premium” client, we have to create a new virtualhost in Apache, create a new directory on the server, then setup the symlinks to point to the core app (we actually have an automated script for this).
If you’re looking to have hundreds or thousands of clients with customisations, then having 200 odd coldbox.cfc files and virtual hosts to manage would become a little messy to say the least.
Ideally, in that scenario, you’d have one wildcard virtualhost, pointing at one directory, with interceptor wrappers or helpers to serve up different views/layouts.
If these 200 odd customised clients need model style customisation instead of just different layouts, then it’s gonna get a little messy again, and much harder –loading different models/interceptors/plugins for each client, you might have to use session-based scope for wireBox to avoid the clashing problem you alluded to earlier (hopefully you have a lot of RAM). You may be able to prepend the “customised” model locations into wirebox on sessionstart. I don’t know. In honesty, I’m realising as I’m typing this that I don’t really know what I’m talking about! I only really know about the solution we’ve implemented, and I’m sure there are much better people around here (especially when it comes to WireBox, which I’ve not got much experience with except using it out-of-the-box) when it comes to multi-tenancy……
Hope that helps!
Actually Tom it wont get that messy at all.
Some years ago, I wrote an interceptor that changed the conventions on the fly. For example
Would share the application, have a different ORM model. The key to this was to use the externals location, if nothining existed in there Coldbox would then load from the normal convention. ORM entities at this point was easy, especially if they need to share common but provide some customisation to the table.