Application Generation

Musings on DSLs, DSM, Agile, SPLs and CFML by Peter Bell

Critical LightWire Error in Setter Injection for Transients

leave a comment »

LightWire is a lightweight Dependency Injection engine for CFML that I wrote a while back and is being used by a number of developers and projects.

Many thanks to Micky Dionisio for finding and documenting a critical error in LightWire. I saw the posting yesterday, but didn’t get a chance to replicate the error and post a patch until this morning.

This posting clarifies what the error is and how to patch it.

Please note this does NOT APPLY to the version of LightWire in ColdBox – which does NOT have this error.

Please feel free to pass this along to others, and if anyone else has a custom copy of LightWire they need a hand with, let me know as I’m more than happy to help out with the patching if anyone has any questions.

Side note – if you haven’t already, please update your feed readers to point to my new blog instead of the old one. It’ll make sure you continue to have access to any postings like this if you want them.

Summarizing the Problems

If you inject a transient into a transient, the injected transient is injected as a singleton – not a transient. This means that any changes you make to one of those transients will affect other instances of it.

Replicating the Problem

Here’s some simple code I threw together (Micky also posted good sample code on his posting)

<code>
index.cfm:
<cfscript>
BeanConfig = createObject(“component”, “scratch.lightwire.BaseConfigObject”).init( );
BeanConfig.addTransient( “scratch.transient1″ , “transient1″ );
BeanConfig.addTransient( “scratch.transient2″ , “transient2″ );
BeanConfig.addSetterDependency( “transient1″ , “transient2″ );
BeanFactory = CreateObject(“component”,”scratch.lightwire.LightWire”).init( BeanConfig );
transient1 = BeanFactory.getBean( “transient1″ );
transient2 = transient1.getTransient2();
writeoutput( “Transient 1 is called #transient1.getName()#<br />” );
writeoutput( “Transient 2 is called #transient2.getName()#<br />” );
transient1.setName( “I am transient 1 and I’ve been changed” );
transient2.setName( “I am transient 2 and I’ve been changed” );
writeoutput( “Transient 1 is called #transient1.getName()#<br />” );
writeoutput( “Transient 2 is called #transient2.getName()#<br />” );
transient1b = BeanFactory.getBean( “transient1″ );
transient2b = transient1b.getTransient2();
writeoutput( “Transient 1 is called #transient1b.getName()#<br />” );
writeoutput( “Transient 2 is called #transient2b.getName()#<br />” );
</cfscript>

transient1.cfc
<cfcomponent name=”transient1″ output=”false”><cfscript>

public transient1 function init() {
variables.instance = structNew();
setName( “I’m transient 1″);
return this;
}

public string function getName() {
return variables.instance.Name;
}

public transient2 function getTransient2() {
return variables.transient2;
}

public string function setName( Name) {
variables.Instance.Name = arguments.Name;
}

public transient1 function setTransient2( Transient2 ) {
variables.Transient2 = arguments.Transient2;
return this;
}
</cfscript></cfcomponent>

transient2.cfc
<cfcomponent name=”transient2″ output=”false”><cfscript>

public transient2 function init() {
variables.instance = structNew();
setName( “I’m transient 2″);
return this;
}

public string function getName() {
return variables.instance.Name;
}

public string function setName( Name) {
variables.Instance.Name = arguments.Name;
}

</cfscript></cfcomponent>
</code>

You’ll see that transient2b has the name assigned to transient 2 – NOT a good thing :-(

The fix is fairly straightforward. In LightWire.cfc there is a method called setterandMixinInject.

It includes the following code:

<code>
// If there are any setter dependencies
If (StructCount(variables.Config[arguments.ObjectName].SetterDependencyStruct))
{
// Inject them all
For (Key in variables.Config[arguments.ObjectName].SetterDependencyStruct)
{
If (key NEQ arguments.ObjectName)
{
evaluate(“arguments.object.set#Config[arguments.ObjectName].SetterDependencyStruct[Key]#(variables.getSingleton(key))”);
};
};
};
</code>

In the evaluate line (line 281 in my version, although it appears there are various versions floating around – something I’ll work on resolving next week), just replace variables.getSingleton(key) with variables.getBean(key). It’ll automatically handle singletons and transients appropriately then (you don’t need to add the conditional logic Micky did – that is what getBean() is for – to handle singletons and transients appropriately – and seamlessly).

There are two associated errors if you’re using addMixinDependency() instead of addSetterDependency().

The first is just a little further down in the same method just after the “MIXIN DEPENDENCIES” comment. It’s in the following code:

<code>

// Get current object name
If (Key NEQ arguments.ObjectName)
{
arguments.object.lightwireMixin(Config[arguments.ObjectName].MixinDependencyStruct[Key], variables.getSingleton(Key));
};

</code>

and again, just replace getSingleton(key) with getBean(key) to fix the error.

The second is a little further down in the method. In the following code:

<code>

// MIXIN DEPENDENCIES (annotations)
MixinInjectionList = arguments.object.lightwireGetAnnotations();
For (Count = 1; Count lte listlen(MixinInjectionList); Count = Count + 1)
{
// Get current object name
LoopObjectName = ListGetAt(MixinInjectionList, Count);
arguments.object.lightwireMixin(LoopObjectName, getSingleton(LoopObjectName));
};
</code>

You want to replace getSingleton(LoopObjectName) with getBean(LoopObjectName).

Here is a patched version of the LightWire.cfc.

The download at RIAForge has also been patched.

Thanks very much Micky for the detailed error report and also Brian Rinaldi for poking me to make sure I was checking it out :-)

FYI, Micky also mentions an issue with a singleton being treated as a transient. I was *not* able to replicate that and do not believe there to be a bug there, but I’ll circle round with Micky just to be sure and will post if any additional issues are found.


Written by peterbell

January 9, 2010 at 2:23 pm

Posted in ColdFusion/CFML

Leave a Reply