jsoncoldfusioncfimport

Execute a function in the context of another cfc in ColdFusion while honoring import statements


Background

I'm trying to create a function componentFromJson that can reconstitute a component graph from JSON. I took a simple approach where I'm using getMetaData in order to lookup the component properties to instantiate the right types.

The function would be used like: comp = componentFromJson(json, 'RootComponentType')

Problem

The problem is that the type of the properties are not necessarily fully qualified because namespaces may have been imported, as we can see below.

<cfimport path="some.namespace.Test">

<cfcomponent>
    <cfproperty name="test" type="Test">
</cfcomponent>

When I'm trying to do createObject('Test') from the componentFromJson function context it's obviously failing because the calling context doesn't have the imports.

I have tried many different ways to resolve the problem, including temporarily defining the component factory function on the parent component dynamically and using invoke to call the factory function in the context of the parent CFC, but it doesn't work.

E.g.

<cfscript>
    parentCmp = createObject('SomeCmp');
    parentCmp.createComponent = function (type) {
        return createObject(type);
    };
    childCmp = invoke(parentCmp, 'createComponent', { type = 'Test' });
</cfscript>

Horrible solution

The only way I can think of solving this issue right now is to parse the ColdFusion code of the CFC and extract the import statements, but I'm expecting this to be way too slow for the purpose. Not only that but this wouldn't cover all edge cases.

Ideas?

I'd like to know if someone has a better idea for solving this issue? Is there an entirely different approach I could take? There is probably a way to do this using the ColdFusion runtime classes, but I haven't figured it out yet.


Solution

  • Well, it turns out that it wasn't so hard when you knew the underlying mechanics of the ColdFusion runtime (which I had trouble to find initially).

    I finally discovered that a ColdFusion component, which is represented as a coldfusion.runtime.TemplateProxy was encapsulating a coldfusion.runtime.CFPage instance which in turns has a createObject method.

    Therefore, here's the solution I came up with using Java reflection:

    <cfset host = new SomeComponent()>
    <cfset pageField = createObject('java', 'coldfusion.runtime.TemplateProxy').getClass().getDeclaredField('page')>
    <cfset pageField.setAccessible(true)>
    <cfset page = pageField.get(host)>
    <cfset test = page.createObject('Test')>