0

Circular Dependency Puzzle

ColdFusion

This entry is the equivalent of me talking this problem out publicly. Perhaps by the end of it I will have my solution!

Aaron and I are working fast and furiously on InstantSpot behind the scenes so that we can put out a rather large 2.0-ish release in the next couple of months. In the scope of this version we came to the conclusion that it was time to remove Reactor and move to concrete classes. As I mentioned a couple blog posts ago, I am using Brian Rinaldi's Illudium PU-36 Code Generator for creating the CFC templates I modified the XSL a bit to fit my needs, and am using ColdSpring to wire them together so that and object's Service not only has its own DAO and Gateway injected into it, but whatever other Services that are necessary for managing relationships to other objects.

For instance, take our Site bean and its helpers: Site.cfc, SiteService.cfc, SiteDAO.cfc, and SiteGateway.cfc.

Now, let's add in our Page bean and its helpers: Page.cfc, PageService.cfc, PageDAO.cfc, PageGateway.cfc.

In our InstantSpot model, Site hasMany Page objects. Fairly straight forward right?

So in my SiteService I have the methods getPageQuery(), which returns a query recordset from the PageService, and getPageArray() which builds an array of the getPageQuery() recordset.

So, if my application were no bigger than this, my ColdSpring configuration would look like this:

Taking that approach, all works well. When I instantiate a Site bean I can successfully call Site.getPageQuery(), Site.getPageArray(), Site.getPageIterator() and everything is just cruising along fine.

whooops! a snag....

Well as we see, Site objects "hasMany" Page objects, but doesn't a Page "hasOne" Site? In our model it does. Well no problem! I will just inject an instance of the SiteService into the PageService so that the PageService can do a call to PageService.getSite(). So, let's add the SiteService argument to the PageService init() and modfiy the ColdSpring config like so:

Now I reinit my application and I see...

java.lang.StackOverflowError

Of course my initial reaction was "Man, ColdSpring can't do that? Come on!" so I decided to manually instantiate the DAOs and Gateways, passing the arguments by hand.

So I typed the following:

Sweet... so now I will just instantiate that SiteService, and pass it an instance of the PageService... hmmm... but I guess I need to instantiate the PageService first, so I will do that and pass it an instance of the SiteService.... wait... ummm....

So yeah... that is my current puzzle. I know it has been solved countless times by others but for some reason I am currently hung up on it. What is it that I am missing in this pattern?

I really like the idea that a Bean can only talk to its own Service and that the Services then talk to each other. I know that feels right. I also know I don't want to have to instantiate these things at runtime in the Services themselves because that would circumvent the benefits of dependency injection.

For the record, this exercise of typing my problem out loud has certainly made it even more painfully clear, but I still don't have an answer that feels right. What am I missing?

 

NOTE: shortly after posting this, Brian Kotek offered the solution in the comments below. Make sure you read his answer. Thanks Brian!

 

tags:
ColdFusion
Brian Kotek said:
 
Dave all you need to do is move to using setter injection instead of constructor injection. ColdSpring will handle the steps necessary to resolve the circular dependencies automatically.
 
posted 737 days ago
Add Comment Reply to: this comment OR this thread
 
 
Jeez... for some reason that option never crossed my mind. Thanks Brian. I am going to go try it now.
 
posted 737 days ago
Add Comment Reply to: this comment OR this thread
 
 
Well that is just ridiculous. I can't believe what an easy solution that was. Time to go change my Illudium templates! Thanks for the input Brian.
 
posted 737 days ago
Add Comment Reply to: this comment OR this thread
 
Mike Kelp said:
 
Hey Dave,

I'm not sure if there is a way to get ColdSpring to do this (as I have not tried), but also remember that you can call create object on both and then your init passing the references of both.

Doug Boude has a great example of this here:
http://www.dougboude.com/blog/1/2007/06/Circular-D...

The reason I bring this up is that yes you could use setter injection, but there have been many times in my applications where I simply do not want someone creating an object without its dependencies satisfied. Though most cases I think the setter injection is very appropriate for keeping a system consistent.

Mike.
 
posted 737 days ago
Add Comment Reply to: this comment OR this thread
 
Brian Kotek said:
 
No, the only mechanism for ColdSpring to resolve circular dependencies is using setter injection.

There is also a problem with doing the CreateObject and init() calls separately instead of chaining them: it isn't thread safe. If you do:

application.foo = CreateObject('component','foo')
application.foo.init()

It is possible for another thread to use the un-constructed application.foo.

Additionally, you really don't need to worry about it if you use ColdSpring. ColdSpring won't give you back an object until all of its dependencies are resolved, whether they are constructor injected or setter injected.
 
posted 737 days ago
Add Comment Reply to: this comment OR this thread
 
Mike Kelp said:
 
Good points Brian!

Thanks,
Mike.

 
posted 737 days ago
Add Comment Reply to: this comment OR this thread
 
WOW gold said:
 
 
posted 73 days ago
Add Comment Reply to: this comment OR this thread
 

Search