Rob Gonda's Blog

cf.objective() 2007 in two weeks

CF.Objective() is coming up. Like I said last time, the speakers lineup is amazing, it's less expensive than most conferences, and you will get tons of advanced topics: Ajax, ColdSpring, Transfer, Flex ... Some brand new Scorpio news ... hope to see you there.
I will be speaking on Flex and the Cairngorm micro-architecture ... fun stuff.

btw, Mark said he will finish my Transfer composite keys support by then, so this post makes is official! :) I can't wait.

Transfer 0.6.3 Released

Transfer 0.6.3 was released today. tql support is now official, and the next one in line is composite keys. Keep an eye on this project! I will be posting more and more about it.

Flex Intensive Course

Extra, extra, read all about it! Flex Wizard in a miracle cure! I'll be teaching a full day pre-conference class June the 25th, at the CFUnited Conference. Last year I taught an Ajax Intensive class, which turned out to be very successful... The feedback I received was: more code, more code! so this year, it's mostly hands-on! You'll be coding in no time.

This full day hands-on Flex course that require no previous Flex experience. The class promises to be fun, energetic, and saturated with information, examples, and code. It will focus not only on Flex architecture, but also teach you to organize your ColdFusion code in such fashion that you can reuse your business layer across any front-end, including Html, Ajax, Flash, or Flex.

Agenda:

  • Flex Builder Environment
  • Flex Built-in Components
  • Basics
    • Event System
    • Data binding
  • ActionScript 3.0
  • Remote Calls: HTTP Services, Web Services, AMF3 / Remoting
    • Organizing your ColdFusion Service Layer
    • Flexible Messaging Architecture
    • Error Handling
    • Logging
  • Flex Data Services
  • Cairngorm Microarchitecture
    • Basics / Request Flow
    • Design Patterns
    • Analogy with ColdFusion Frameworks
  • Debugging
    • Within Flex
    • Monitoring HTTP / AMF Traffic
  • Documentation
  • Automated Deployment using ANT

p.s. if you're still wondering about the first line of this post, it's a quote from Tommy, by The Who.

CFUnited Express Atlanta 2007

I'll be speaking this Thursday at CFUnited Express Atlanta with well renowned speakers such as Charlie Arehart, Ben Forta, Hal Helms, Pau Bonfanti, and Andrew Powell. If you're in the neighborhood make sure to stop by and check it out. Hopefully I'll have some time to meet old friends such as JesterXL and Mike Britton ... See you all there.

cf.objective() 2007 sessions posted

CF.Objective() updated the sessions list, and let me tell you, it looks amazing! This will be the best high-level CF event of the year, so I hope to see you there.
I'll be speaking on Flex and Cairngorm Microarchitecture, a fresh new topic which should interest most CF developers.

MAX 2007

It's official, Ben posted the MAX 2007 dates and venues:

  • MAX North America: September 30-October 3, 2007, Chicago, IL
  • MAX Europe: October 2007, Barcelona, Spain
  • MAX Japan: November 2007, Japan
I've never made a Max and I really hope this is my lucky year.

Object Factories and Circular Dependencies

In my last post about Object Factories I provided very basic examples to get you up and running quickly, and mentioned that it would not work with circular dependencies. A circular dependency means that two objects depend on each other. If you try to create them and pass each other on the constructor (init) method, you will create an infinity loop.

E.g.

objA = createObject('component', 'objA').init( objB = get('objB');
this will resolve into getting objB
objB = createObject('component', 'objA').init( objA = get('objA');
this will try to create objA, but since that action hasn't completed yet, it will create a new one, and keep creating new objects recursively.

So what's the solution? using setters methods instead of constructor methods. This simply means that you would first create an object, and then once created, pass all dependencies by executing setObjA, setObjB, setObjN...

Here's the new Galleon object factory that allows for circular dependencies

<!---
    Name         : objectFactory.cfc
    Author       : Rob Gonda
    Created      : August 25, 2006
    Last Updated : February 22, 2007
    History      :
    Purpose         : Simple Object Factory / Service Locator
--->
<cfcomponent displayname="objectFactory" hint="I am a simple object factory">

    <!---
        function init
        in:       
        out:    this
        notes:    usually initialized in application
     --->
    <cffunction name="init" access="public" output="No" returntype="objectFactory">
   
        <cfscript>
            // persistance of objects
            variables.com = structNew();
        </cfscript>

        <cfreturn this />
    </cffunction>

    <!---
        function getObject
        in:        name of object
        out:    object
        notes:   
     --->
    <cffunction name="get" access="public" output="No" returntype="any">
        <cfargument name="objName" required="false" type="string" />
        <cfargument name="singleton" required="false" type="boolean" default="true" />
       
        <cfscript>
            var obj = ''; //local var to hold object
            if (arguments.singleton and singletonExists(arguments.objName)) {
                return getSingleton(arguments.objName);
            }
       
            switch(arguments.objName) {
                case "conference":
                    obj = createObject('component','conference').init();
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                        // inject dependencies through setter
                        obj.setSettings( get('galleonSettings', arguments.singleton) );
                        obj.setForum( get('forum', arguments.singleton) );
                        obj.setUtils( get('utils', arguments.singleton) );
                    return obj;
                break;

                case "forum":
                    obj = createObject('component','forum').init();
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                        // inject dependencies through setter
                        obj.setSettings( get('galleonSettings', arguments.singleton) );
                        obj.setThread( get('thread', arguments.singleton) );
                        obj.setUtils( get('utils', arguments.singleton) );
                    return obj;
                break;

                case "galleonSettings":
                    obj = createObject('component','galleon');
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                    return obj;
                break;

                case "message":
                    obj = createObject('component','message').init();
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                        // inject dependencies through setter
                        obj.setSettings( get('galleonSettings', arguments.singleton) );
                        obj.setThread( get('thread', arguments.singleton) );
                        obj.setForum( get('forum', arguments.singleton) );
                        obj.setConference( get('conference', arguments.singleton) );
                        obj.setUser( get('user', arguments.singleton) );
                        obj.setUtils( get('utils', arguments.singleton) );
                    return obj;
                break;

                case "rank":
                    obj = createObject('component','rank').init();
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                        // inject dependencies through setter
                        obj.setSettings( get('galleonSettings', arguments.singleton) );
                        obj.setUtils( get('utils', arguments.singleton) );
                    return obj;
                break;

                case "thread":
                    obj = createObject('component','thread').init();
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                        // inject dependencies through setter
                        obj.setSettings( get('galleonSettings', arguments.singleton) );
                        obj.setUtils( get('utils', arguments.singleton) );
                        obj.setMessage( get('message', arguments.singleton) );
                    return obj;
                break;

                case "user":
                    obj = createObject('component','user').init();
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                        // inject dependencies through setter
                        obj.setSettings( get('galleonSettings', arguments.singleton) );
                        obj.setUtils( get('utils', arguments.singleton) );
                    return obj;
                break;

                case "utils":
                    obj = createObject('component','utils');
                        if (arguments.singleton) { // scope singleton
                            addSingleton(arguments.objName, obj);
                        }
                        // inject dependencies through setter
                    return obj;
                break;

            }
        </cfscript>
       
    </cffunction>
   
   
    <cffunction name="singletonExists" access="public" output="No" returntype="boolean">
        <cfargument name="objName" required="Yes" type="string" />
        <cfreturn StructKeyExists(variables.com, arguments.objName) />
    </cffunction>
   
    <cffunction name="addSingleton" access="public" output="No" returntype="void">
        <cfargument name="objName" required="Yes" type="string" />
        <cfargument name="obj" required="Yes" />
        <cfset variables.com[arguments.objName] = arguments.obj />
    </cffunction>

    <cffunction name="getSingleton" access="public" output="No" returntype="any">
        <cfargument name="objName" required="Yes" type="string" />
        <cfreturn variables.com[arguments.objName] />
    </cffunction>

    <cffunction name="removeSingleton" access="public" output="No" returntype="void">
        <cfargument name="objName" required="Yes" />
        <cfscript>
            if ( StructKeyExists(variables.com, arguments.objName) ){
                structDelete(variables.com, arguments.objName);
            }
        </cfscript>
    </cffunction>

</cfcomponent>


You will notice that I didn't try to create the object, execute the setters, and then return it. Instead, I create the object, check if I should scope it as a singleton, scope it, and then execute the setters. The reason for this is to break the infinite recursion. I need to scope it before my setters try to get a new/existing instance of an object. Once I scope it, the next object will be able to get it w/o having to complete the full initialization.

I sent Ray Camden my version of Galleons a few months ago as an exercise, but I intentionally left out a couple of wires to break circular dependencies. I assume Ray checked the code and fixed the pieces of code where I cheated, creating the circular dependencies... but the factory I sent did not support this. Here's the updated one, so expect to see it at the Galleon RIAForge page soon.

Reminder: South Florida CFUG Tonight

Intro to Object Factories with slides and code

I presented at the Frameworks Conference a session called Intro to Object Factories and I forgot/did not have time to post the slides and code. For those of you new to this concept, an object factory is nothing but a component whose sole job is to create components. An object factory is quite simple and small. Its main role is to collect the information necessary to create an instance of the intended object's class and then to invoke that class's constructor.

In the conference I talked and explained all the boring theory behind it, but what seemed to be the most effective part of the session is the one where I showed code, and I followed step by step on how to create and use one.... so let's jump straight to it.

To illustrate my point, I chose to use Ray Camden's Galleon forums. I've used this forums and I like the code, however, Ray could have done a better job wiring the component dependencies. As a side note, I am actually glad to see that I made a difference, and Ray is in the process of adapting some factory patterns and perhaps even add them to Galleons in the future.

So back to the code, with the download, you will find four copies of the Galleon forums, all working, but all using different approaches when it comes to creating, initializing, and wiring the components (Download the slides and source code)

For lack of a better name, I named the folders forums, forums2, forums3, and forums4.
Forums: Original Galleon Forums
Forums2: Small Object Factory with Dependency Injection
Forums3: Small Object Factory without Dependency Injection
Forums4: ColdSpring



Let's start with Forums3. If you look at Application.cfm and compare it to Forums, you would note that I initialized an application.factory, which subsequently I use to create the rest of the objects that reside in the application scope. Note that I did not want to modify any of the Galleon's core, controllers, views, so I left all the code intact with the exception of the cfcs. In a different scenario, you may not even have to scope all these components in the application, but instead just get them from the factory when needed.

<!--- get user CFC --->
    <cfset application.factory = createObject("component","cfcs.objectFactory").init()>

    <!--- Get main settings --->
    <cfset application.settings = application.factory.getInstance('galleonSettings').getSettings()>

    <!--- get user CFC --->
    <cfset application.user = application.factory.getInstance('user')>

    <!--- get utils CFC --->
    <cfset application.utils = application.factory.getInstance('utils')>

    <!--- get conference CFC --->
    <cfset application.conference = application.factory.getInstance('conference')>

    <!--- get forum CFC --->
    <cfset application.forum = application.factory.getInstance('forum')>

    <!--- get thread CFC --->
    <cfset application.thread = application.factory.getInstance('thread')>

    <!--- get message CFC --->
    <cfset application.message = application.factory.getInstance('message')>

    <!--- get rank CFC --->
    <cfset application.rank = application.factory.getInstance('rank')>

The factory looks as follows

<!---
    Name         : objectFactory.cfc
    Author       : Rob Gonda
    Created      : August 25, 2006
    Last Updated : August 25, 2006
    History      :
    Purpose         : Simple Object Factory / Service Locator
--->
<cfcomponent displayname="objectFactory" hint="I am a simple object factory">

    <!---
        function init
        in:       
        out:    this
        notes:    usually initialized in application
     --->
    <cffunction name="init" access="public" output="No" returntype="objectFactory">
   
        <cfscript>
            // persistance of objects
            variables.com = structNew();
        </cfscript>

        <cfreturn this />
    </cffunction>

    <!---
        function getObject
        in:        name of object
        out:    object
        notes:   
     --->
    <cffunction name="createObj" access="public" output="No" returntype="any">
        <cfargument name="objName" required="Yes" />
       
        <cfscript>
            switch(arguments.objName) {
                case "conference":
                    return createObject('component','conference').init(this);
                break;

                case "forum":
                    return createObject('component','forum').init(this);
                break;

                case "galleonSettings":
                    return createObject('component','galleon');
                break;

                case "message":
                    return createObject('component','message').init(this);
                break;

                case "rank":
                    return createObject('component','rank').init(this);
                break;

                case "thread":
                    return createObject('component','thread').init(this);
                break;

                case "user":
                    return createObject('component','user').init(this);
                break;

                case "utils":
                    return createObject('component','utils');
                break;

            }
        </cfscript>
       
    </cffunction>
   
   
    <!---
        function getInstance
        in:        name of object
        out:    object
        notes:    create a persistant object if doen not previously exists
     --->
    <cffunction name="getInstance" access="public" output="No" returntype="any">
        <cfargument name="objName" required="Yes" />
       
        <cfscript>
            if ( not StructKeyExists(variables.com, arguments.objName) ){
                variables.com[arguments.objName] = createObj(arguments.objName);
            }
           
            return variables.com[arguments.objName];
        </cfscript>
    </cffunction>

    <cffunction name="removeInstance" access="public" output="No" returntype="void">
        <cfargument name="objName" required="Yes" />
   
        <cfscript>
            if ( StructKeyExists(variables.com, arguments.objName) ){
                structDelete(variables.com, arguments.objName);
            }
        </cfscript>
    </cffunction>

</cfcomponent>

It has a few basic functions:
init: initializes the factory... does nothing except creating a holder for the components and returning itself.

createObj:
has a big switch/case that knows how to initialize all the components, depending on the object name passed to the factory. This method will not store/scope the component, but simple return an initialized instance. Note that the only argument passed to the components is 'this', which is a pointer back to itself. All components will be 'factory-aware', which means that they know about the factory, and if they need any other component they should just ask the factory to provide them with one.

getInstance:
this method simply checks if a component has been initialized; if it has, it returns its instance, and if it has not, it would create a new instance, scope it, and return it. This method ensures that every time you request an instance of an object, it will return the same one... this is know as a singleton pattern. (in strict design patterns, there would be more to this, but let's just run with the very basic concept).

removeInstance:
I created this method for commodity, so I can remove an instance for the factory and consequently, initializing a new one the next time it is invoked.

Now that we've seen the factory, let's look at one of the objects it creates.

<cfcomponent displayName="Forum" hint="Handles Forums which contain a collection of threads.">

    <cfset variables.dsn = "">
    <cfset variales.dbtype = "">
    <cfset variables.tableprefix = "">
       
    <cffunction name="init" access="public" returnType="forum" output="false"
                hint="Returns an instance of the CFC initialized with the correct DSN.">

        <cfargument name="factory" required="true" hint="factory">

        <cfset variables.factory = arguments.factory>
        <cfset variables.dsn = arguments.factory.getInstance('galleonSettings').getSettings().dsn>
        <cfset variables.dbtype = arguments.factory.getInstance('galleonSettings').getSettings().dbtype>
        <cfset variables.tableprefix = arguments.factory.getInstance('galleonSettings').getSettings().tableprefix>

        <cfset variables.thread = arguments.factory.getInstance('thread') />
        <cfset variables.utils = arguments.factory.getInstance('utils') />

        <cfreturn this>
       
    </cffunction>
..........
</cfcomponent>

You will note that the init method receives a single arguments: the instance of the factory. It stores a pointer to this instance, and uses it to request the settings object, and well as thread, and utils. As you can see, the forum object does not know where the other objects are, or how to initialize them. There is also a single instance of each one of these objects in memory, inside the factory to be thorough.

By doing this, we reduced the complexity. There's now a single place that knows where the objects are located and how to initialize them.

But now, every object depends on the factory ... there is nothing really wrong with this approach, but it can be improved. Your objects are coupled now to a single object, instead of to each other, but this approach is hard to unit-test, and it makes the components a little less portable.

Let's examine now the 'forums2' folder. You will immediately notice the similarity.. Application.cfm looks the same, there is an objectFactory.cfc... but this time, instead of passing the factory to all objects, it passes the dependencies to them, which is known as Dependency Injection.

Take a look at the new factory

<!---
    Name         : objectFactory.cfc
    Author       : Rob Gonda
    Created      : August 25, 2006
    Last Updated : August 25, 2006
    History      :
    Purpose         : Simple Object Factory / Service Locator
--->
<cfcomponent displayname="objectFactory" hint="I am a simple object factory">

    <!---
        function init
        in:       
        out:    this
        notes:    usually initialized in application
     --->
    <cffunction name="init" access="public" output="No" returntype="objectFactory">
   
        <cfscript>
            // persistance of objects
            variables.com = structNew();
        </cfscript>

        <cfreturn this />
    </cffunction>

    <!---
        function getObject
        in:        name of object
        out:    object
        notes:   
     --->
    <cffunction name="createObj" access="public" output="No" returntype="any">
        <cfargument name="objName" required="Yes" />
       
        <cfscript>
            switch(arguments.objName) {
                case "conference":
                    return createObject('component','conference').init(
                        settings = getInstance('galleonSettings'),
                        forum = getInstance('forum'),
                        utils = getInstance('utils')
                    );
                break;

                case "forum":
                    return createObject('component','forum').init(
                        settings = getInstance('galleonSettings'),
                        thread = getInstance('thread'),
                        utils = getInstance('utils')
                    );
                break;

                case "galleonSettings":
                    return createObject('component','galleon');
                break;

                case "message":
                    return createObject('component','message').init(
                        settings = getInstance('galleonSettings'),
                        thread = getInstance('thread'),
                        forum = getInstance('forum'),
                        conference = getInstance('conference'),
                        user = getInstance('user'),
                        utils = getInstance('utils')
                    );
                break;

                case "rank":
                    return createObject('component','rank').init(
                        settings = getInstance('galleonSettings')
                    );
                break;

                case "thread":
                    return createObject('component','thread').init(
                        settings = getInstance('galleonSettings'),
                        utils = getInstance('utils')
                    );
                break;

                case "user":
                    return createObject('component','user').init(
                        settings = getInstance('galleonSettings'),
                        utils = getInstance('utils')
                    );
                break;

                case "utils":
                    return createObject('component','utils');
                break;

            }
        </cfscript>
       
    </cffunction>
   
   
    <!---
        function getInstance
        in:        name of object
        out:    object
        notes:    create a persistant object if doen not previously exists
     --->
    <cffunction name="getInstance" access="public" output="No" returntype="any">
        <cfargument name="objName" required="Yes" />
       
        <cfscript>
            if ( not StructKeyExists(variables.com, arguments.objName) ){
                variables.com[arguments.objName] = createObj(arguments.objName);
            }
           
            return variables.com[arguments.objName];
        </cfscript>
    </cffunction>

    <cffunction name="removeInstance" access="public" output="No" returntype="void">
        <cfargument name="objName" required="Yes" />
   
        <cfscript>
            if ( StructKeyExists(variables.com, arguments.objName) ){
                structDelete(variables.com, arguments.objName);
            }
        </cfscript>
    </cffunction>

</cfcomponent>

Nothing much changed, except that when initializing an object, it passes instances of other objects instead of the factory.

Let's look at the same forum object.

return createObject('component','forum').init(
    settings = getInstance('galleonSettings'),
    thread = getInstance('thread'),
    utils = getInstance('utils')
);

The factory is passing to the forum init function an instance of settings, thread, and utils. Because you're calling the getInstance method of the factory itself, it will run that code and get the instance of those objects prior to initializing the forum object. This method will recursively execute itself until initializing all dependencies, scope them all inside the factory, maintain a singleton of each, and then resolve and return back the requested object.

The forum object now does not receive the factory as an argument, but instead, it receives an instance of each object that it needs.

<cfcomponent displayName="Forum" hint="Handles Forums which contain a collection of threads.">

    <cfset variables.dsn = "">
    <cfset variales.dbtype = "">
    <cfset variables.tableprefix = "">
       
    <cffunction name="init" access="public" returnType="forum" output="false"
                hint="Returns an instance of the CFC initialized with the correct DSN.">
        <cfargument name="settings" required="true" hint="Setting">
        <cfargument name="thread" required="true" hint="thread">
        <cfargument name="utils" required="true" hint="utils">
       
        <cfset variables.dsn = arguments.settings.getSettings().dsn>
        <cfset variables.dbtype = arguments.settings.getSettings().dbtype>
        <cfset variables.tableprefix = arguments.settings.getSettings().tableprefix>

        <cfset variables.thread = arguments.thread />
        <cfset variables.utils = arguments.utils />
        <cfreturn this>
       
    </cffunction>
.............
</cfcomponent>


Now you see why this pattern is called Dependency Injection... don't let the name scare you away, all it is doing is passing single instances each object that is dependent on another.

Hang on, we're almost there... 3 out of 4 examples shown.

The last one, is similar to the one we just examined... it uses the Dependency Injection pattern, but instead of a home-grown factory object, we are going to use ColdSpring.

Open your forums4 folder.

Application.cfm is very similar to the one we just saw, except that in ColdSpring the getInstance method is called getBean. Bean is nothing but an object, and the name comes from the Java world.

ColdSpring does not define the dependencies inside a CFC, but instead it allows you to configure them using an external XML file, which is passed to the ColdSpring component when initialized.

    <!--- create a ColdSpring bean factory --->
    <cfset application.factory = createObject("component","coldspring.beans.DefaultXmlBeanFactory").init()>

    <!--- load in our bean definitions --->
    <cfset application.factory.loadBeans(expandPath("./config/ColdSpring.xml"))/>

    <!--- Get main settings --->
    <cfset application.settings = application.factory.getBean('galleonSettings').getSettings()>

&nbs

IMified powered by ColdFusion and AjaxCFC

Imified is an instant messenger buddy that offers access to productivity tools like notes, reminders, and todo's. The site is powered by ColdFusion and their IM bot is done through CFMX7 Event Gateways. However, the most important business aspect is that their back-end / account management is all AjaxCFC! :->
The concept is great because it's simple, yet very handy... I wish them luck and hope they add many more features.

I also wrote a few bots before, but quickly ran into licensing issues with the networks. All networks restrict the amount of messages a single user can send/receive per day, and some networks charge up to $50,000 simply to allow your bot to live.

More Entries

This blog is running version 5.9.003. Contact Blog Owner