Autowire bug

21 views
Skip to first unread message

Dan Vega

unread,
Jan 13, 2010, 9:38:31 PM1/13/10
to framework-one
So I was messing around with fw1 tonight and I came across a bug. I
was not quite sure where to post it because I actually wrote a fix so
its not really an issue anymore.

I had a controller that looked like this.

component accessors="true" {

property AuthorService;

public Author function init(fw){
variables.fw = arguments.fw;
return this;
}

public void function list(){
rc.authors = AuthorService.list();
}
}

Whenever I tried to access the authorservice it would come up
undefined. I set the authorService setter attribute to false and wrote
my setter. Not to my surprise it worked just fine. I jumped into the
framework and found the autowire method. The problem is the way we are
looping through the components methods here. After talking with a
friend he pointed out that they are not really methods, which was
right along the lines I was thinking. So running autowire would never
see my setter, therefore never inject my service.


<cffunction name="autowire" access="private" output="false"
hint="Used to autowire controllers and services from a bean
factory.">
<cfargument name="cfc" />
<cfargument name="beanFactory" />

<cfset var key = 0 />
<cfset var property = 0 />
<cfset var args = 0 />

<cfloop item="key" collection="#arguments.cfc#">
<cfif len(key) gt 3 and left(key,3) is "set">
<cfset property = right(key, len(key)-3) />
<cfif arguments.beanFactory.containsBean(property)>
<!--- args = [ getBeanFactory().getBean(property) ] does not seem
to be portable --->
<cfset args = structNew() />
<cfset args[property] = arguments.beanFactory.getBean(property) /
>
<cfinvoke component="#arguments.cfc#" method="#key#"
argumentCollection="#args#" />
</cfif>
</cfif>
</cfloop>

</cffunction>


A quick rewrite using getMetaData() and it works just great now!

<cffunction name="autowire" access="private" output="false"
hint="Used to autowire controllers and services from a bean
factory.">
<cfargument name="cfc" />

<cfset var key = 0 />
<cfset var property = 0 />
<cfset var args = 0 />
<cfset var methods = getMetaData(arguments.cfc).functions>

<cfloop array="#methods#" index="method">
<cfif structKeyExists(method,"name")>
<cfif len(method.name) gt 3 and left(method.name,3) is "set">
<cfset property = right(method.name, len(method.name)-3) />
<cfif getBeanFactory().containsBean(property)>
<!--- args = [ getBeanFactory().getBean(property) ] does not
seem to be portable --->
<cfset args = structNew() />
<cfset args[property] = getBeanFactory().getBean(property) />
<cfinvoke component="#arguments.cfc#" method="#method.name#"
argumentCollection="#args#" />
</cfif>
</cfif>
</cfif>
</cfloop>

</cffunction>

Dutch Rapley

unread,
Jan 13, 2010, 10:04:18 PM1/13/10
to framew...@googlegroups.com
On Wed, Jan 13, 2010 at 9:38 PM, Dan Vega <dan...@gmail.com> wrote:
So I was messing around with fw1 tonight and I came across a bug.

Whenever I tried to access the authorservice it would come up
undefined.

It's not really a bug. As you mentioned, it has to do with how Adobe ColdFusion 9 handles "implicit" getters and setters. Adobe CF 9 doesn't implement implicit getters and setters, it creates something along the lines of dynamic responders for getters and setters based on property metadata. Autowire loops through the collection of functions and and wires accordingly. Using property metadata in CF9 doesn't add "implicit" getters/setters to the collection, hence why it doesn't work. Too bad CFCs don't have a built in respondTo() method.
 
I had pinged Sean about the same issue off list a while back and suggested a similar fix.

He doesn't use getMetaData() because it doesn't handle inheritance. I'm using subsystems, and in some instances, I'm inheriting a couple levels deep: user < SubsystemController < ApplicationController. In this instance, getMetaData() would break my application.

-Dutch

Bob Silverberg

unread,
Jan 13, 2010, 10:49:23 PM1/13/10
to framew...@googlegroups.com
Just an fyi - I ran into a similar problem trying to use metadata to work with properties and the metadata not returning inherited properties.  I wrote a little function to return all properties for the current object, plus all of it's ancestors.  I blogged about it, but here's the code:

private array function collectAllProperties(required struct md,array props=ArrayNew(1)) {
    local.prop = 1;
    if (structKeyExists(arguments.md,"properties")) {
        for (local.prop=1; local.prop <= ArrayLen(arguments.md.properties); local.prop++) {
            if (not ArrayContains(arguments.props,arguments.md.properties[local.prop].name)) {
                arrayAppend(arguments.props,arguments.md.properties[local.prop]);
            }
        }
    }
    if (arguments.md.extends.fullname neq "WEB-INF.cftags.component") {
        arguments.props = collectAllProperties(arguments.md.extends,arguments.props);
    }
    return arguments.props;
}

It's written in CF9 script syntax, so won't work as is in earlier versions, but I think the concept is a good one.

Cheers,
Bob


--
You received this message because you are subscribed to the Google Groups "framework-one" group.
To post to this group, send email to framew...@googlegroups.com.
To unsubscribe from this group, send email to framework-on...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/framework-one?hl=en.




--
Bob Silverberg
www.silverwareconsulting.com

Hands-on ColdFusion ORM Training @ cf.Objective() 2010
www.silverwareconsulting.com/cformtraining/


Dan Vega

unread,
Jan 13, 2010, 10:50:38 PM1/13/10
to framework-one
That is unfortunate because I just found another instance and I expect
I will find more. The populate method does the same loop.

On Jan 13, 10:04 pm, Dutch Rapley <dutch.rap...@gmail.com> wrote:

Dutch Rapley

unread,
Jan 14, 2010, 9:23:12 AM1/14/10
to framew...@googlegroups.com
On Wed, Jan 13, 2010 at 10:50 PM, Dan Vega <dan...@gmail.com> wrote:
That is unfortunate because I just found another instance and I expect
I will find more.

I don't think it's that unfortunate for auto wiring controllers. Auto wiring doesn't happen on every request, only when the framework loads the controllers, when the framework is first initialized (or if you're using subsystems, only the first time each subsystem is loaded). Controllers are singletons and cached during framework initialization.
 
The populate method does the same loop.

populate() doesn't loop through the cfc if you pass a list of keys, but it still performs a structKeyExists() on the cfc.

If you're uncomfortable with how the built in populate() method works, you could always roll your own and add it to Application.cfc. You'll still call it in your controllers with fw.populate(). This will override the framework's built in method. YMMV.

-Dutch

 

Dan Vega

unread,
Jan 14, 2010, 9:40:35 AM1/14/10
to framework-one
Ya, I have a AbstractDomain that I use that has a populate method, i
just went with that.

On Jan 14, 9:23 am, Dutch Rapley <dutch.rap...@gmail.com> wrote:


> On Wed, Jan 13, 2010 at 10:50 PM, Dan Vega <danv...@gmail.com> wrote:
> > That is unfortunate because I just found another instance and I expect
> > I will find more.
>
> I don't think it's that unfortunate for auto wiring controllers. Auto wiring
> doesn't happen on every request, only when the framework loads the
> controllers, when the framework is first initialized (or if you're using
> subsystems, only the first time each subsystem is loaded). Controllers are
> singletons and cached during framework initialization.
>
> > The populate method does the same loop.
>

> populate() doesn't loop through the cfc *if* you pass a list of keys, but it

Sean Corfield

unread,
Jan 14, 2010, 6:25:41 PM1/14/10
to framew...@googlegroups.com
On Wed, Jan 13, 2010 at 7:04 PM, Dutch Rapley <dutch....@gmail.com> wrote:
> It's not really a bug. As you mentioned, it has to do with how Adobe
> ColdFusion 9 handles "implicit" getters and setters. Adobe CF 9 doesn't
> implement implicit getters and setters, it creates something along the lines
> of dynamic responders for getters and setters based on property metadata.

I think Adobe have made a mistake in their approach to this - but I
understand why they took this approach. Essentially, they can generate
faster code for setters / getters if they're not relying on real
functions. I think the better approach would have been to generate
real functions but to still use the native (high-performance)
implementation. This would allow all the existing code out there that
matches collections of data against public setXyz() methods in
components to work without having to resort to getMetadata() on the
component and everyone having to code their own solution to the
inheritance issue.

And you're right that both autowire() and populate() use a similar
approach, requiring real methods.

The metadata approach simply won't work in the presence of runtime
method injection - which is certainly possible for transient objects
that you might want to use populate() on. Runtime injection is how
ColdBox works with models, BTW, although it uses a custom metadata
DSL.

That said, current approaches that walk the CFC for public setXyz()
methods don't take onMissingMethod() into account either...
--
Sean A Corfield -- (904) 302-SEAN
Railo Technologies US -- http://getrailo.com/
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

Dan Vega

unread,
Feb 5, 2010, 2:34:11 PM2/5/10
to framework-one
If I rewrite the base of the framework to use getMetaData() where am I
going to run into issues? I understand trade offs here and there but
if I going to have real issues then I won't do it. I just love
getters / setters written for me, I don't like bloating my components
with these methods.

Dan

On Jan 14, 6:25 pm, Sean Corfield <seancorfi...@gmail.com> wrote:

> Railo Technologies US --http://getrailo.com/
> An Architect's View --http://corfield.org/

Dutch Rapley

unread,
Feb 5, 2010, 3:52:14 PM2/5/10
to framew...@googlegroups.com
Dan,

If you're going to use some form of a "base" controller that your other controllers extend, you'll run into issues.

This is exactly what I do, it minimizes the amount redundant getters and setters that I have to write. By the time I get to the controller at hand, I'm only dealing with a couple of auto wire getters and setters.

-Dutch

--
You received this message because you are subscribed to the Google Groups "framework-one" group.
To post to this group, send email to framew...@googlegroups.com.
To unsubscribe from this group, send email to framework-on...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/framework-one?hl=en.




--
Dutch Rapley
www.dutchrapley.com

Dan Vega

unread,
Feb 24, 2010, 12:00:43 PM2/24/10
to framework-one
So Sean, if you read this I have a quick question. Using Bob's method
we should be able to handle inheritance with getMetadata. Any chance
on updating the core of the framework to use getmetadata? It would
just be nice to declare a property and automatically have the service
injected.

I updated my local copy of fw1 and it works fine but I don't want to
do this on our production box because if I ever go to update the
framework it could cause issues.

> > framework-on...@googlegroups.com<framework-one%2Bunsubscribe@goog legroups.com>

Ryan Cogswell

unread,
Feb 24, 2010, 12:11:12 PM2/24/10
to framew...@googlegroups.com
On Wed, Feb 24, 2010 at 11:00 AM, Dan Vega <dan...@gmail.com> wrote:
> So Sean, if you read this I have a quick question. Using Bob's method
> we should be able to handle inheritance with getMetadata. Any chance
> on updating the core of the framework to use getmetadata?

Sean already addressed this question earlier in this same thread (see
below). Bob's method deals with inheritance, but not runtime method
injection.

Dan Vega

unread,
Feb 24, 2010, 12:43:26 PM2/24/10
to framework-one
I am just talking about the auto wire method. Maybe I am just not
understanding something here but where in this framework am I every
going to auto wire a transient object into a controller or a service?
My service creates my objects and hands it off to the controller.


On Feb 24, 12:11 pm, Ryan Cogswell <ryancogsw...@gmail.com> wrote:

Sean Corfield

unread,
Feb 25, 2010, 10:19:07 PM2/25/10
to framew...@googlegroups.com
On Wed, Feb 24, 2010 at 9:43 AM, Dan Vega <dan...@gmail.com> wrote:
> I am just talking about the auto wire method. Maybe I am just not
> understanding something here but where in this framework am I every
> going to auto wire a transient object into a controller or a service?
> My service creates my objects and hands it off to the controller.

Metadata doesn't address onMissingMethod(), nor injected setters (on
your controllers or services), nor methods declared via <cfinclude>'d
files, nor several other scenarios. It would only address CF9 property
set/get methods if special, CF9-specific code was added to look for
the additional metadata for those - rather than the regular methods
metadata present on CF8 and Railo and OpenBD.

Looping over public methods doesn't address onMissingMethod() nor CF9
property set/get methods - so it addresses more scenarios.

Since you are only worried about services and controllers, I'm
surprised you think this is a big deal? The CF9 property stuff is
intended mainly for ORM / transient objects...


--
Sean A Corfield -- (904) 302-SEAN

Railo Technologies US -- http://getrailo.com/
An Architect's View -- http://corfield.org/

Reply all
Reply to author
Forward
0 new messages