Categories
Contact Doug!
Learn About Doug!
View Doug Boude's online resume
updated 11/18/2009

View Doug Boude's profile on LinkedIn
Link to me!

Follow Doug Boude on Twitter
Follow me!

Be Doug's friend on Facebook
Befriend me!
(I promise not to follow you home)
OO Lexicon
Chat with Doug!
Recent Entries
You may also be interested in...
Web Hosting

<< May, 2013 >>
SMTWTFS
1234
567891011
12131415161718
19202122232425
262728293031
Search Blog

Recent Comments
Re: Disappearing IE Popup Window During Save/Open Dialog (by LZ at 4/20 7:58 AM)
Re: Create Dynamic WHERE Clauses in PHP (by pooja at 3/20 7:29 AM)
Re: Just What IS a 'Service Layer', Anyway? (by EugenK at 3/07 7:56 PM)
Re: Using Google as your CF Mail Server (by 5starwebteam.com at 2/25 1:27 AM)
Re: Why Provide for Service layer objects in CFWheels? (by Steven Benjamin at 1/25 11:43 AM)
Re: What is an 'Advanced' Coldfusion Developer? (by ColdFusion Developer at 12/24 5:14 AM)
Re: Equivalent of SQL "TOP X" in Oracle (by Ashenafi Desalegn at 12/06 5:29 AM)
Re: PHP Export to Excel Snippet (by serene at 12/05 1:44 AM)
Re: Just What Is 'Application Logic', Anyway? (by Arif at 11/13 8:06 AM)
Re: Hosts File Changes Not Acknowledged on Vista 64 (by Aaron at 10/22 2:31 PM)
Re: PHP Export to Excel Snippet (by Jafar Shah at 10/10 4:28 AM)
Re: Viewing Option Text (in IE7) that's Wider than the Select List (by Chenelle S at 10/04 12:53 PM)
Re: PHP Export to Excel Snippet (by Kilo at 9/26 5:20 PM)
Re: Porting Coldfusion Code to Mura (by tariq at 9/03 9:51 AM)
Re: Just What IS a 'Service Layer', Anyway? (by James at 8/27 4:06 PM)
Re: Calculating Business Hours (by helen at 8/14 2:54 AM)
Re: What IS 'Business Logic', Anyway? (by dougboude at 8/06 11:30 AM)
Re: What IS 'Business Logic', Anyway? (by Adrianne at 8/06 10:29 AM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by dougboude at 8/04 4:39 PM)
Re: Family Law: The Weapon of Choice for Woman Scorned (by Lola LB at 8/04 7:43 AM)
Archives
Photo Albums
Funnies (5)
Family (3)
RSS

Powered by
BlogCFM v1.11

29 July 2008
How to Think about Controller objects and Model objects
with Coldspring in the mix!

Even though MVC has been "all the rage" now for a good long time and several frameworks have been lovingly and painstakingly developed and shared by the CF community to help facilitate the migration to a more standardized OO approach to development, I do believe that there will always be a need to regularly return to the foundational principles of our craft. For some, an affirmation or further perfecting of their core thought processes; for others, the first exposure to a new way of thinking about and viewing the tools they wield as a problem solver and programmer. After having immersed oneself so deeply in MVC and OO for a substantial length of time and embracing those concepts so completely, these foundational principles can become so much a part of us that we hardly even recognize them as such, and tend rather to simply see them as a part of who we are and how we think. Sometimes, though, the privileged occasion arises when we get to once again see them as the wondrous, shiny ideas that they are, when a person who has had the tenaciousness and fortitude to embark upon that OO journey asks us to share our own insight in order to facilitate the evolution of their own assimilation of these concepts.

Alright, perhaps a bit too poetic a prelude, but I was recently asked to share my own understanding of the relationship between controller objects and model objects and the mechanics there. I couldn't help but get a little excited as I took the time to examine my own understanding of the subject and attempt to relate it to this person and realized just how much a part of my thinking it has become. Of course, it goes without saying that I know for a fact I still have a lot of room for growth in this area, but I do feel at least a sense of satisfaction that the couple of years I've invested so far in this has grown me more technically than I believe I had in the five years prior doing procedural coding.

 Okay, enough with the nostalgia already! Here's the question I was asked:

[10:09 PM]  hey Doug-
[10:10 PM]  thanks so much for all the tutorials and learning materials, can't thank you enough
[10:11 PM]  i've got a basic model glue site up and running with one exception, total MVC with not much in the model, totally bloated controllers. Still trying to get my head around calling and using the code from my model via the controller, any blog entries or anecodotal stories to suggest that would guide me?

My Response:

Hey, you're welcome for the tutorials and such. I definitely don't do it for the fame, but rather because I KNOW how painful it can be to traverse a new learning curve, espcially when it comes to frameworks, OO, and the like. If I can spare someone even ten minutes of hair pulling then I've accomplished what I set out to do! :)
 
Using your model code in your controllers (which is exactly what a person should be doing :) )...this is nothing more or less than giving your controller cfc an instance of your model cfc so it can manipulate it, call its methods, etc. For instance, imagine for a moment that we are attempting to log a person in to our app (framework doesn't matter, all we know is it's one that supports MVC). The user has just filled in their username and password and clicked the submit button, and those two pieces of info are now handed off to our controller via whatever mechanism the framework we're using has provided us. We have created for ourselves a Security.cfc that has a method called "Login".

 
<cfcomponent displayname="Security">
   <cffunction name="login">
       <cfargument name="username" />
       <cfargument name="password" />
    ...
   </cffunction>
</cfcomponent>


 
Now, Security is our controller, so by virtue of that fact the general rule of thumb is that the controller's job is to be nothing more than a liaison between the View (our forms and results page) and our model (where the real work gets done!), our controller cfc should have very little code in it and in fact should be using our model object to get the real work done. In order for it to do that, it needs an instance of it's Model security counterpart. In our scenario, we have a CFC living in the Model directory also called Security which performs the actual act of attempting authentication. Here's what our Model/Security.cfc might look like:
 

<cfcomponent displayname="Security">
   <cffunction name="login">
       <cfargument name="username" />
       <cfargument name="password" />
       <cfset var results = false /><!--- set the value of our return variable. by default we're going to return a result of false, meaning the authentication failed --->
       <cfquery name="qryLogin">
          select username from user_table where username = <cfqueryparam value="#arguments.username#" cfsqltype="cf_sql_varchar" /> and password = <cfqueryparam value="#arguments.password#" cfsqltype="cf_sql_varchar" />
       </cfquery>
       <cfif qryLogin.recordcount eq 1><!--- we authenticated! --->
            <cfset results = true />
       </cfif>
       <cfreturn results />
   </cffunction>
</cfcomponent>

 
Makes sense to you, I'm sure...same kind of code we've been using for decades to authenticate a person, right? Okay, so now we need for our controller to actually make the login call. Let's give our Security controller access to this functionality. Here's our controller Security CFC again...
 

<cfcomponent displayname="Security">
   <cffunction name="login">
       <cfargument name="username" />
       <cfargument name="password" />
       <cfset var objSecurity = createobject("component","Model.Security") />
       <cfset var results = false />
       <cfset results = objSecurity.login(username=arguments.username,password=arguments.password) />
       <cfreturn results />
   </cffunction>
</cfcomponent>

 
So, within our controller, we have created an instance of our Model security CFC and called its login method. I know this is a very simplified example, but it is precisely the way to think about the relationship between your model and your controller objects! We can get a little fancier if we like and put our object into the variables scope within our CFC so that we only create one instance of it and then let every method in the controller access it as needed, like this:
 

<cfcomponent displayname="Security">
 <cffunction name="init">
  <cfset variables._objSecurity = createobject("component","Model.security") />
 </cffunction>
 <cffunction name="getSecurity">
  <cfreturn variables._objSecurity />
 </cffunction>
 <cffunction name="login">
    <cfargument name="username" />
    <cfargument name="password" />
    <cfset var results = false />
    <cfset results = getSecurity().login(username=arguments.username,password=arguments.password) />
    <cfreturn results />
 </cffunction>
</cfcomponent>


 
Now any method in my controller that needs to use the model security object need only do a "getSecurity()" call! This is assuming that your Init method is getting called when your controller is instantiated by your framework; it would need to in order for the security object to be present and available.
 
Now, for many reasons, it is not the best idea to be hard coding object instantiation into your controllers. I mean, what if you did that and then somewhere down the road you had to make a change to your Model/security.cfc that now required the init method to be called upon instantiation? You would have to hunt down every place in your app where the security object was being instantiated and change the createobject line to include the init method call. In this scenario probably not a huge deal to do so; but potentially this could create a LOT of extra maintenance. This specific thing is why I have come to love Coldspring so much. It allows me to "inject" an instance of my model/security.cfc into my controller/security.cfc in one handy place so that I need never (probably) touch my controller when a change is made to my model as I described. Here is my controller when I inject the model/security object using Coldspring:
 

<cfcomponent displayname="Security">
 <!--- Coldspring Autowire methods --->
 <cffunction name="setSecurity" access="public" returntype="void" output="false">
  <cfargument name="Security" required="true" type="any" />
  <cfset variables._Security = arguments.Security />
 </cffunction>
 <cffunction name="getSecurity" access="private" returntype="any" output="false">
  <cfreturn variables._Security />
 </cffunction>
 
 <cffunction name="login">
    <cfargument name="username" />
    <cfargument name="password" />
    <cfset var results = false />
    <cfset results = getSecurity().login(username=arguments.username,password=arguments.password) />
    <cfreturn results />
 </cffunction>
</cfcomponent>

 
By adding Coldspring to the mix, when it instantiates your controllers and finds corresponding SET and GET methods like that, it will look to see if it has a definition for the object the set and get references (in this case, the set and get are for 'Security'...getSECURITY, setSECURITY...so it will be looking for a bean definition named SECURITY). If it does, it will go ahead and create that object and inject it into the controller for you so that it is "present and accounted for" from that moment on. So, I could theoretically define a whole bunch of different objects (aka 'beans') for Coldspring, and then when I need one of those objects in my controller, all i gotta do is create a SET and GET method that matches the object name. Let's say our Security controller ALSO needs to be able to send emails, and our app has an Emailservice CFC in the model. Well heck, let's just inject that bad boy and use it! :
 

<cfcomponent displayname="Security">
 <!--- Coldspring Autowire methods --->
 <cffunction name="setSecurity" access="public" returntype="void" output="false">
  <cfargument name="Security" required="true" type="any" />
  <cfset variables._Security = arguments.Security />
 </cffunction>
 <cffunction name="getSecurity" access="private" returntype="any" output="false">
  <cfreturn variables._Security />
 </cffunction>
 
 <cffunction name="setEmailService" access="public" returntype="void" output="false">
  <cfargument name="EmailService" required="true" type="any" />
  <cfset variables._EmailService = arguments.EmailService />
 </cffunction>
 <cffunction name="getEmailService" access="private" returntype="any" output="false">
  <cfreturn variables._EmailService />
 </cffunction>
 
 <cffunction name="login">
    <cfargument name="username" />
    <cfargument name="password" />
    <cfset var results = false />
    <cfset results = getSecurity().login(username=arguments.username,password=arguments.password) />
    <cfif not results>
      <cfset getEmailService().sendAdminEmail(subject="failed login") />
    </cfif>
    <cfreturn results />
 </cffunction>
</cfcomponent>

 
Coldspring just makes object injection SO simple, don't you think? Setters and getters are one way of injecting objects using Coldspring; it also can do it another way, which I won't get into right now (no need to muddy the waters at this juncture). Now, you should also know that there are times when individual Model objects ALSO need to use instances of other model objects (you may have encountered this already at some point). For instance, the example of an Emailservice object...by virtue of the very defnition of a service object (which I do have a blog post about), it is likely manipulating the methods of two or more OTHER objects. Just like our controller, for our model object to use another object, it has to have an instance injected into it somehow. Either we created that instance manually using a Createobject statement, OR we leveraged Coldspring to auto-inject those instances for us. The manner in which it is done is exactly the same as it is for controllers, no significant differences at all.
 
Okay, I know I've kinda gone on and on about this, but (if you can't tell) I do tend to have a passion for Coldspring and the beauty it brings to an OO application! But you know, even if you opt not to leverage Coldspring to perform the object injection and choose rather to do it manually (which I, for one, would not frown upon you for doing :) ), the concept is exactly the same: If one object needs to manipulate another object,  you must create an instance of the needed object(s) within the controlling object, whether that happens to live in the model or controller. Having said that, there are a few hard and fast rules of thumb when writing these apps that I always follow and which you may want to consider for yourself.
 
1. A controller's job is to be liaison between view and model, only. Keep controller methods as light as possible, doing as little work as possible.
2. A controller should NEVER talk to the database
3. A controller is the ONLY one who has the privilege of talking to Session or Application scopes (unless you have yourself a session facade object, which I personally have never seen the need for. If you DO have a session facade object, then IT is the only object in the app who has the privilege of talking to session. in that case, the session facade will have a corresponding controller and model object).
4. Model objects should be doing as much of the app's work as possible. In a nutshell, build your model objects as if tomorrow you would be told that you had to change frameworks and your model was the only thing you could take with you.
5. NEVER even consider injecting a controller object into a model object. BLAH! YUK! PTOOEY! In fact, never allow yourself the sacrelige of injecting one CONTROLLER into another. If you're seeing the possibility of a need to do that, then probably you need to refactor some logic and move some code around.
 
That's about it. Have I helped fill in any gaps? Have I opened any more? Did I address what it is you were needing? Let me know, I don't mind taking the time to share (and learn)!
 
Doug  :0)

 

 

 

Posted by dougboude at 9:31 AM | PRINT THIS POST! | Link | 2 comments



24 July 2008
Using Special Characters within Coldspring.XML

Just because a person uses Model Glue and has learned enough about XML to know how to create basic settings doesn't necessarily mean that they have a strong command of XML, right? Well, I needed to put a funky, "special character filled" value into my global config object for use later, and through some experimentation found that using XML's 'CDATA' allowed me to do this so I thought I'd share it since I hadn't seen it used in Coldspring.xml before. Allow the picture to speak volumes:

  <bean id="GlobalConfig" class="ModelGlue.Bean.CommonBeans.SimpleConfig">
    <property name="Config">
      <map>
        <entry key="DNS"><value>myDNS</value></entry>
  <entry key="FusionMapDir"><value>/FusionMaps</value></entry>
  <entry key="FusionJS"><value>/js/FusionMaps.js</value></entry>
  <entry key="FusionChartDir"><value>/FusionCharts</value></entry>
  <entry key="FusionChartJS"><value>/js/FusionCharts.js</value></entry>
  <entry key="PDFRoot"><value>/images/PDFMaps</value></entry>
  <entry key="adminEvents"><value>page.admin,page.datamgmt,page.entityadmin,page.dataadmin</value></entry>
       <entry key="chartSubCaption"><value><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Asia Matters for America\nwww.asiamattersforamerica.org\n]]></value></entry>
   </map>     
    </property>
  </bean>

I am using a third party Flash-based charting tool and needed to apply a global sub caption to all charts produced, so decided to store it in my Coldspring.xml as part of my global config bean.

Now, I will say that I first attempted to pass in that subcaption value within my modelglue.xml file as an argument, like so:

 

 <event-handler name="getChart">
   <broadcasts>
    <message name="getChartData">
     <argument name="qryName" value="qryChartData" />
     <argument name="entityTypeID" value="3" />
    </message>
    <message name="getChartXML">
     <argument name="subcaption" value="&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Asia Matters for America\nwww.asiamattersforamerica.org\n" />
    </message>
   </broadcasts>
   <results />
   <views>
    <include name="returnChart" template="dspBigChart.cfm" />
   </views>
  </event-handler>

Of course, that turned my xml invalid. So I tried doing it like this:

 

  <event-handler name="getChart">
   <broadcasts>
    <message name="getChartData">
     <argument name="qryName" value="qryChartData" />
     <argument name="entityTypeID" value="3" />
    </message>
    <message name="getChartXML">
     <argument name="subcaption" value="<![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Asia Matters for America\nwww.asiamattersforamerica.org\n]]>" />
    </message>
   </broadcasts>
   <results />
   <views>
    <include name="returnChart" template="dspBigChart.cfm" />
   </views>
  </event-handler>

Still invalid, so i just gave up and went with the coldspring.xml approach for time's sake. Does anybody know the correct way (if there is one) to utilize CDATA within an XML attribute value like I tried to do? I probably won't stray from the way I'm doing it now in my global config, but I am curious.

Posted by dougboude at 12:09 AM | PRINT THIS POST! | Link | 0 comments
12 December 2007
Model-Glue Ambiguous Error Matrix
As any user of Model-Glue can attest to, there are often times when you get the ol' "OOPS!" message and it doesn't give you much of a clue as to what the REAL issue is. Well, in order to help myself save time during future development and decipher these rather vague and even misleading types of errors, I've begun keeping track of them. I capture the error itself, what I found the TRUE cause to be, and what I did to rectify it. I hope to one day turn the matrix into more of a Model-Glue knowledgebase of sorts, but for now here it is in its basic state, in hopes that it may help someone else save a little hair pulling.

Bear in mind that since the errors I encounter are generated by my own code, some of the details will be reflective of that code. Just look past code specifics and into the heart of the matter in order to find what, within your OWN application, spawned your non-informative Model-Glue error.

May I also encourage all of you Model-Glue developers to do a similar thing as you encounter such ambiguous and frustrating errors. If you'll forward those to me (dougboude att geemale dote calm) along with a cause and fix, I'll gladly add them for the benefit of the community!

Without further adieux, my "Ambiguous Model-Glue Error Matrix" as it currently exists:


Error TextCauseFix

Oops!

Message An error occured while Searching an XML document.

Detail Empty expression!

Extended Info Tag Context C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (144) C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (159) C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (159) C:\ColdFusion8\wwwroot\reactor\core\objectTranslator.cfc (99) C:\ColdFusion8\wwwroot\reactor\core\objectFactory.cfc (214) C:\ColdFusion8\wwwroot\reactor\reactorFactory.cfc (80) C:\ColdFusion8\wwwroot\ModelGlue\unity\orm\ReactorAdapter.cfc (57) C:\ColdFusion8\wwwroot\ModelGlue\unity\controller\GenericORMController.cfc (113) C:\ColdFusion8\wwwroot\ModelGlue\unity\listener\Listener.cfc (26) C:\ColdFusion8\wwwroot\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (359) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (332) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (306) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (264) C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm (104) C:\ColdFusion8\wwwroot\Housefacks\index.cfm (50)

Case sensitivity issue when using Reactor with or without a Modelglue Generic Event. The generic commit lists the value as "ImageRecord"; the actual object exists as "imageRecord".

Portion of the event calling the generic commit: 
<message name=""ModelGlue.genericCommit"">
    <argument name=""recordName"" value=""ImageRecord"" />
    <argument name=""criteria"" value=""imageID"" />
    <argument name=""object"" value=""image"" />
</message>

reactor.xml corresponding entry:
<object name="hf_image" alias="image">
    <hasOne name="Report">
        <relate from="ReportID" to="ReportID" />
    </hasOne> 
</object>

Make sure that case matches on every front. Generic event's recordname value; generic event's object value; alias name in reactor.xml for that object; actual name of record object generated by Reactor. Correct all case values, put Reactor into Development mode (within coldspring.xml), then re-execute the event that broadcasts the generic message, ensuring that "&init=true" is appended to the url. This will regenerate the object with the proper case and everything will be in sync again.
The web site you are accessing has experienced an unexpected error. Please contact the website administrator. The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request Model-Glue XML Problem: Bad tag. Every must have a NAME attribute.
The Event definition was malformed in the modelglue.xml file (or included xml file). Here is the offending event:
<event-handler name=""inspector.saveprefs"" access=""public"">
 
<broadcasts>
  <message name=""ModelGlue.genericCommit"">
   <argument name=""recordName"" value=""ClientRecord"" />
   <argument name=""criteria"" value=""ClientID"" />
   <argument name=""object"" value=""Client"" />
   <argument name=""validationName"" value=""PreferenceValidation"" />
  </message>
  <results>
   <result name=""commit"" do=""Inspector.ClientDetails"" redirect=""true"" append="""" preserveState=""false"" />
   <result name=""validationError"" do=""Inspector.ClientDetails"" redirect=""false"" append="""" preserveState=""true"" />
  </results>
  <views />
 </broadcasts>
</event-handler>

Ensure that the event definition is well formed. Even though from an XML standpoint it may be valid, it must also be valid from a Modelglue standpoint as well.

Oops!

Message The cause of this output exception was that: coldfusion.tagext.sql.QueryParamTag$InvalidDataException: Invalid data value [invite to parrty] exceeds maxlength setting [50]..

Detail

Extended Info

Tag Context C:\ColdFusion8\wwwroot\reactor\project\housefacks\Dao\ClientDao.cfc (694) C:\ColdFusion8\wwwroot\reactor\project\housefacks\Dao\ClientDao.cfc (13) C:\ColdFusion8\wwwroot\reactor\base\abstractRecord.cfc (148) C:\ColdFusion8\wwwroot\reactor\base\abstractRecord.cfc (139) C:\ColdFusion8\wwwroot\reactor\base\abstractRecord.cfc (129) C:\ColdFusion8\wwwroot\ModelGlue\unity\orm\ReactorAdapter.cfc (526) C:\ColdFusion8\wwwroot\ModelGlue\unity\controller\GenericORMController.cfc (139) C:\ColdFusion8\wwwroot\ModelGlue\unity\listener\Listener.cfc (26) C:\ColdFusion8\wwwroot\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (359) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (332) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (306) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (264) C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm (104) C:\ColdFusion8\wwwroot\Housefacks\index.cfm (50)

Occurs during attempted insert/update/commit, when the value of submitted form field contains more characters than the table field allows. This entry present because it CAN occur even when invoking Reactor's auto-validation. The reason this error could be seen WITH validation in place is due to the finicky nature of Reactor and how and when it regenerates its base objects. If the base object was not regenerated after the table had been modified, then no validation exists for any new fields that were added and we get this mostly untrapped error. Solution:
  • make sure reactor is in Development mode.
  • reinit the app while still in dev mode, walk through the portion of your app that performs validation
  • switch back to production mode for reactor
  • reinit the app

Oops!

Message Routines cannot be declared more than once.

Detail The routine setState has been declared twice in the same file. The CFML compiler was processing: A cffunction tag beginning on line 395, column 10. Extended Info

Tag Context C:\ColdFusion8\wwwroot\reactor\project\housefacks\Record\UserRecord.cfc (395) C:\ColdFusion8\wwwroot\reactor\core\objectFactory.cfc (145) C:\ColdFusion8\wwwroot\reactor\reactorFactory.cfc (26) C:\ColdFusion8\wwwroot\Housefacks\model\Authentication.cfc (62) C:\ColdFusion8\wwwroot\Housefacks\controller\AuthenticationController.cfc (25) C:\ColdFusion8\wwwroot\ModelGlue\unity\listener\Listener.cfc (26) C:\ColdFusion8\wwwroot\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (359) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (332) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (306) C:\ColdFusion8\wwwroot\ModelGlue\unity\framework\ModelGlue.cfc (264) C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm (104) C:\ColdFusion8\wwwroot\Housefacks\index.cfm (50)

Added a relationship to Reactor.xml. Attempted to relate a User's state ID value to a record in the States table in order to be able to retrieve the state abbreviation as opposed to the state ID.
<object name="hf_User" alias="User">
    <hasMany name="Role">
        <link name="UserRole" />
    </hasMany>  
    <hasMany name="Report">
        <relate from="UserID" to="InspectorID" />
    </hasMany>
    <hasMany name="Client" >
        <relate from="UserID" to="UserID" />
    </hasMany>
    <hasMany name="Realtor">
        <relate from=""UserID"" to="UserID" />
    </hasMany>
    <hasOne name="membership">
        <relate from="membershipID" to="membershipID" />
    </hasOne>
    <hasOne name="State">
        <relate from="state" to="stateID" />
    </hasOne>
</object>

The PROBLEM is that User already has a field named "State", and since the States table is also called State, Reactor is attempting to create two methods with the identical name...one that would retrieve the value of the State FIELD, and one that would retrieve the State OBJECT. This violates the rules.
SOLUTION: Use the attribute "alias" within the hasOne or hasMany tag to alias the method that will be generated. Like so:
<hasOne name="State" alias="ST">
    <relate from="state" to="stateID" />
</hasOne>

An error occured while Parsing an XML document.

The element type "value" must be terminated by the matching end-tag "".

The error occurred in C:\ColdFusion8\wwwroot\coldspring\beans\DefaultXmlBeanFactory.cfc: line 87 Called from C:\ColdFusion8\wwwroot\coldspring\beans\DefaultXmlBeanFactory.cfc: line 63 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm: line 71 Called from C:\ColdFusion8\wwwroot\Housefacks\index.cfm: line 50 85 : 86:00:00 87 :

88 : 89:00:00
ID10T error. Accidentally erased part of a tag in Coldspring.xml
look at the change that was just made and correct the malformed tag. In this case, it was a property tag:
 
<property name="mode"><value>production/value></property>
 

Model-Glue: C:\ColdFusion8\wwwroot\catschedule\scheduler\config\security.xml isn't valid XML!

The error occurred in C:\ColdFusion8\wwwroot\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 76 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 99 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\loader\FrameworkLoader.cfc: line 98 Called from C:\ColdFusion8\wwwroot\ModelGlue\unity\ModelGlue.cfm: line 116 Called from C:\ColdFusion8\wwwroot\catschedule\scheduler\index.cfm: line 72

 
           74 : </p>
            <cfset cfg = xmlParse(cfg).xmlRoot />    
            75 :            <cfcatch>    
            76 :                    <cfthrow message="Model-Glue: #configurationPath# isn't valid XML!" />    
            77 :            </cfcatch>    
            78 :    </cftry>    
Mal-formed modelglue.xml (or included configuration xml file). If you've looked it over carefully and your tags are all balanced, you might try also verifying that you're using the same CASE for all tag names. XML is case sensitive, so "value" is not the same as "Value". It is recommended that you not make TOO many changes to your modelglue configuration file before testing it, otherwise tracking down the offending line may be a very frustrating and time consuming task.
Oops!   
   
Message    An error occured while Parsing an XML document.
Detail    The content of elements must consist of well-formed character data or markup.
Extended Info   
Tag Context    D:\web\housefacks.com\www\reactor\core\objectTranslator.cfc (91)
    D:\web\housefacks.com\www\reactor\core\objectFactory.cfc (214)
    D:\web\housefacks.com\www\reactor\reactorFactory.cfc (80)
    D:\web\ModelGlue\unity\orm\ReactorAdapter.cfc (271)
    D:\web\ModelGlue\unity\orm\ReactorAdapter.cfc (145)
    D:\web\ModelGlue\unity\orm\ReactorAdapter.cfc (250)
    D:\web\ModelGlue\unity\controller\GenericORMController.cfc (73)
    D:\web\ModelGlue\unity\listener\Listener.cfc (26)
    D:\web\ModelGlue\unity\eventrequest\MessageBroadcaster.cfc (32)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (359)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (332)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (306)
    D:\web\ModelGlue\unity\framework\ModelGlue.cfc (264)
    D:\web\ModelGlue\unity\ModelGlue.cfm (104)
    D:\web\housefacks.com\www\index.cfm (50)
This error was thrown by Reactor when it attempted to XMLParse a dictionary file for a given table. What has probably happened is you are using SVN in your development cycle, and when you performed an update to the destination code, SVN did a kind of "merge" on one of your dictionary files and flubbed it all up for you, thinking it was helping. Because the merged file is now not valid XML, Reactor barfed on it. Replace the target/faulty dictionary file with what's in the repository rather than attempt to merge it.
Bean creation exception in model.client

Cannot declare local variable homehistory twice.:Local variables cannot have the same names as parameters or other local variables.:

Line: -1   The error occurred in C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\BeanDefinition.cfc: line 400
Called from C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\BeanDefinition.cfc: line 215
Called from C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\DefaultXmlBeanFactory.cfc: line 566
Called from C:\ColdFusion8\wwwroot\MyApp\coldspring\beans\DefaultXmlBeanFactory.cfc: line 495
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 293
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 210
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 58
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\XmlConfigurationLoader.cfc: line 74
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\loader\FrameworkLoader.cfc: line 73
Called from C:\ColdFusion8\wwwroot\MyApp\ModelGlue\unity\ModelGlue.cfm: line 94
Called from C:\ColdFusion8\wwwroot\MyApp\index.cfm: line 50

398 :                           <cfthrow type="coldspring.beanCreationException"
399 :                                   message="Bean creation exception in #getBeanClass()#"
400 :                                   detail="#cfcatch.message#:#cfcatch.detail#:#additionalInfo#">
401 :                   </cfcatch>
402 :           </cftry>
The model.client CFC had a local variable named the same as one of the arguments

<cfargument name="myVar"... />
<cfset var myVar = "foo" />
Rename either the local variable or the argument.
Posted by dougboude at 11:20 AM | PRINT THIS POST! | Link | 4 comments
10 June 2007
Global Configuration Settings in Model-Glue:Unity
Some Suggestions
When making the transition from procedural CF to OO, sometimes what was SO simple before can become a challenge (at least the first time). For instance, the way we used to pass around our app's DSN value was probably something along the lines of setting an application variable in our application.cfm or cfc, thus making it accessible throughout our app while consolidating the value to a single location for ease of change later. But now that we've decided to force ourselves to learn to do things in an OO fashion, where on earth is the "correct" place, or what is the "correct" way to perform this same action?

In our procedural apps, common sense told us to "find a place within the execution path that always gets called, no matter what, and check for and set your global value there...". Let's use that same line of thinking for our OO app, too, and find an appropriate spot in which to set our global variable.

Even in an OO approach, CF still respects application.cfc/cfm, so setting the value there could be an option. However, one of the chief tenets of OO is encapsulation, meaning that we try to make each aspect of the application as non-dependent as possible on the rest of the application. Setting our DSN in application.cfc/cfm then means that likely our urge would be to stuff it into the application scope:

<cfset application.dsn = "Horton" />

If I want to use that value within any of my model CFCs then, SOMEWHERE I'd have to write a line of code that talks to the application scope; not a best practice, and indeed immediately makes my CFC dependent on the existence of some value outside of itself. No bueno.

How 'bout instead we utilize a configuration bean...a tiny object whose sole purpose in life is to pack around global values I might need here and there, and to make itself available anywhere within my app? Now that makes my common sense tingle with delight. Utilizing Model-Glue:Unity, let me share a couple of ways I have done this very thing.

At this juncture, if you aren't familiar with the member of the Model-Glue: Unity trinity called Coldspring, it's time you two met. Coldspring is an expert bean handler, and is quite a versatile fellow when it comes to creating beans and making them available wherever you'll be needing them. Coldspring does all of what it does based on the content of its XML configuration file (Coldspring.XML), so it is within that file where we will 'instruct' it regarding our configuration values.

At this point you're probably thinking to yourself, "okay self, all I need to do then is create a CFC that handles configuration values for my app. I'll make a getter and setter method for my DSN, one for my images directory path, one for....". Hey, that is definitely a good way of thinking. HOWEVER, Coldspring has a surprise for you, O Best Beloved. Coldspring can automagically create that configuration bean for you, and all you gotta do is tell it what you want it to contain via the Coldspring.XML file! Allow me to
elucidate.

In order to auto-create one of these configuration beans, let's define it within Coldspring.XML, like so:
  <bean id="AppConfiguration" class="ModelGlue.Bean.CommonBeans.SimpleConfig">
    <property name="Config">
      <map>
    <!-- this is the DSN used throughout the app... -->
        <entry key="DSN"><value>Horton</value></entry>
    <!--  path the CSS directory -->
        <entry key="CSSPath"><value>/appCSS/</value></entry>
      </map>     
    </property>
  </bean>
(Note: Notice the class of our configuration bean...we're leveraging a built-in class of model-glue)

Now that we've defined it, from this moment onward we'll maintain our app settings RIGHT THERE. If the DSN changes, I'll change the value of the DSN entry key.

Making this configuration bean available throughout the app is a purposeful endeavor, meaning that you have to explicity tell Coldspring to make it available to a specific CFC. We do this by "injecting" it into other CFCs that we've told Coldspring about. Looky here:

  <bean id="EmailService" class="model.EmailService">
    <constructor-arg name="AppConfiguration">
      <ref bean="AppConfiguration" />
    </constructor-arg>
  </bean>

I've told Coldspring "I have an EmailService.CFC, and it is expecting an argument value named 'AppConfiguration', so when you instantiate EmailService for me, please go ahead and pass in an instance of my AppConfiguration bean as that value. Thanks man."

Okay, the only other thing you have to do in order for this to work smoothly is to ensure that the object you're wanting to inject the configuration bean into is prepared to receive it. With the approach we're taking, the proper way to do this is to make sure your CFC has an init method, and within that init method create a required argument named exactly the same as your constructor-arg name. Like so:

<cffunction name="init" returntype="EmailService" output="false" hint="Constructor">
    <cfargument name="AppConfiguration" required="true" />
    <cfset variables._config = arguments.AppConfiguration />
    <cfreturn this />
</cffunction>

Okay, so now we have our Coldspring simple config object injected. Pay attention, this is important: for every value we defined for this config object, you will access it via the following method: getConfigSetting([value name]). For instance, if within our EmailService object we have a method that needs the DSN value, we'll grab it with
<cfset myDSN = variables._config.getConfigSetting("DSN") />

Alternatively (and this is what I did), you can create a generic "GetConfigSetting" method within your recipient object, like so:

<cffunction name="GetConfigSetting" access="private" returntype="string" output="false">
    <cfargument name="name" required="true" type="string" />
    <cfreturn variables._config.getConfigSetting(arguments.name) />
</cffunction>

With that method present in your recipient object, you would access the DSN like this:

<cfset myDSN = GetConfigSetting("DSN") />


It's a little cleaner, I suppose.

Now, you could be thinking that since your config object contains values that may also be needed within a view, such as a CSS path, or an images directory path, that you'll need a way to make it available there, too. Let me toss out a suggestion based on the way I have done it.

Everytime a MG:U event is called, you can have listeners that execute beforehand based on the fact that they're listening for a call to "OnRequestStart", as in this snippet from my modelglue.XML file:


<controller name="MyController" type="controller.Controller">
   <message-listener message="OnRequestStart" function="loadConfiguration" />
</controller>

You can, within the method being referenced in onRequestStart, stuff the configuration bean into the event object, thus making it available to every other object referencing that event, INCLUDING your views! Let's look at the controller object mentioned in this example:

<cfcomponent displayname="Controller" extends="ModelGlue.unity.controller.Controller" output="false">
    <!--- Autowire / private getter --->
    <cffunction name="setAppConfiguration" access="public" returntype="void" output="false">
        <cfargument name="AppConfiguration" required="true" type="any" />
        <cfset variables._config = arguments.AppConfiguration />
    </cffunction>
    <cffunction name="getAppConfiguration" access="private" returntype="any" output="false">
        <cfreturn variables._config />
    </cffunction>


    <!--- Place the configuration bean into the viewstate --->
    <cffunction name="loadConfiguration" access="public" returnType="void" output="false">
      <cfargument name="event" type="any">
      <cfset arguments.event.setValue("appConfiguration", getAppConfiguration()) />
    </cffunction>
</cfcomponent>

A few things to point out here:
1. Because this object is a controller object, Coldspring gives us the privilege of autowiring in beans that it knows about by simply providing it with a "get" and "set" method named according to the bean we want to inject. In this instance, Coldspring already had created for us a bean called AppConfiguration, so by virtue of the fact that Coldspring saw our controller object had a method named "getAppConfiguration" and "setAppConfiguration", it assumed we wanted it to inject an instance of the AppConfiguration bean. Coool.
2. On request start, Model-Glue is calling our "loadConfiguration" method, which is taking the injected configuration bean and placing it into the Event bucket (some like to call it the event 'bus', but thinking of it as a bucket makes more sense to me). This automatically means that the configuration bean will be available in our viewstate as well, and from within any view can be referenced like


<cfset appConfig = viewstate.getValue("appConfiguration") />

<link rel="stylesheet" type="text/css" href="<cfoutput>#appConfig.getConfigSetting("CSSPath")#</cfoutput>styles.css" media="screen" />

Now, having shared all of that, I will tell you that if you're using Reactor within your app, you were already defining the DSN within the Coldspring Reactor Bean, and may not want to define the DSN twice, but rather would like to use Reactor's DSN value throughout your app. This can be done just as easily, only you'll be injecting not your appConfiguration bean into the CFCs that need the DSN value, but the "reactorConfiguration" bean instead (already exists within your Coldspring.xml file). Once injected, you'll get at your DSN value with a method called "getDSN()".

Here's something else to chew on that may be helpful to you (as it was for me): Besides just a global application config bean, you can also define configuration beans for different services that your app has. For instance, my app performs image manipulation, so within my Coldspring.xml file I defined a configuration bean called "ImageServiceConfig" that contains settings for thumbnail dimensions, large image dimensions, naming conventions, pathing information, etc., then I inject that bean into my ImageService.cfc. I do the same thing for my EmailService.cfc. This allows me to truly consolidate all of my application settings within my Coldspring.xml, simplifying maintenance in that regard.

That, Boys and Girls, is injection at its finest, and it allows me to maintain my app's global values in one place, just like I used to do with application.cfc/cfm.

Doug out.
Posted by dougboude at 9:58 AM | PRINT THIS POST! | Link | 8 comments
05 March 2007
Coldspring Automagic Lesson
Okay, I spent the greater part of today at work wrestling with a situation involving the injection of objects into other objects using Coldspring. After far too many hours of experimentation, furrowed brow, taking up my teammate's time, and frustration, I came to some conclusions. I can't explain the "WHY's" of the conclusions, but i can definately validate the conclusions themselves by my results.

Here's the scenario:

I have object B which extends object A. Object A has an INIT method, object B does not. Object B is being injected into object C. In my Coldspring.xml, I define a bean for object A, B, and C. Object A has a property value ("thisURL") being set via a <property> and <value> tag; object C has object B injected via a <property> and <ref> tag.  (see illustration and coldspring.xml snippet)

The Challenge (there are no problems, only challenges): When all objects are instantiated, I am calling method Y on object C. Method Y itself refers to a method X in the injected object B. Method X in injected object B refers to the property "thisURL" that was set via Coldspring within object A. WE GET AN ERROR, HOWEVER, INDICATING THAT THE VALUE IN OBJECT A WAS NEVER SET!

The Experimentation:
Hmmm, let's put a CFABORT tag in the "setThisURL" method in object A (the method needed by Coldspring in order to 'auto-inject'), with a 'ShowError' attribute. The abort is never executed, meaning that Coldspring never called the 'setThisURL' method on object A, despite the fact that we configured it to do so.

Okay, let's move everything we need into object B, remove the "Extends" attribute from the cfcomponent tag, re-do our Coldspring.XML to inject the 'thisURL' property directly into object B. Hey, it works!

Let's move it all back again, and try adding an init method to object B that performs a "Super.init()" to see if that fixes it. NOPE.

In my mind, in the mind of my peers, configuring Object A to have a property value injected should have resulted in Coldspring executing the "setThisURL" method. In fact, I NEED it to do that, because I plan on utilzing this base class (object A) by extending it with other objects as well, and the constant 'thisURL' needs to be present and the same in all cases. However, it did not behave in the expected manner.

As I stated at the beginning, I have NO idea why it did not behave as I expected. But, I thought I'd share the results of my headache in case it helps someone else avoid one later.

Conclusion:
The only way to make this work was to have Coldspring set the 'thisURL' property directly in object B if I wanted to be able to access it after having extended object A.

Illustration and Coldspring.xml snippet:


Snippet:
<bean id="objectA" class="model.objectA" >
    <property name="thisURL">
          <value>http://my.testserver.com/myWS.cfc?wsdl</value>
    </property>
</bean>

<bean id="
objectB" class="model.objectB" />

<bean id="
objectC" class="model.objectC" >
     <property name="objectB">
         <ref bean="objectB" />
    </property>   
</bean>

(back to top)
Posted by dougboude at 12:00 AM | PRINT THIS POST! | Link | 11 comments