This year's cf.Objective() is offering SIX pre-conference classes! You can choose from Building Secure CFML Applications, ColdBox: 100 Training, Developing Applications with ColdFusion 9 ORM, Getting Started with Flex / AIR Development, Mach-II / OOP from the Ground Up, Rapid Development with Model-Glue 3! Wow!
Some of these are one-day courses (Wednesday April 21st) and some are two-day courses (Tuesday April 20th and Wednesday April 21st).
That means that if you signed up for Jason's talk, you're now signed up for Terry's advanced talk and if you signed up for Adobe's "super secret" Friday talk at 1:45pm, you're now signed up for Jason's basic ORM talk!
Feel free to visit the online scheduler to update your selections if necessary!
After CFUNITED 2008, Liz has promised lots of changes and improvements and we already know that the venue is something special and an all-in-one location (addressing a bit complaint about the last few years' conferences). One of the new changes for 2009 is that a quarter of the content will be Flex/AIR related, acknowledging the growth and increasing relevance of these technologies to ColdFusion developers at large.
Some of the highlights (from my point of view) of the topics announced so far:
- Flex development with the Swiz framework - Chris Scott
- Railo Open Source - Gert Franz
- Groovy for ColdFusion Developers - Joe Rinehart
- iPhone Apps + Adobe ColdFusion - Josh Adams
- ColdFusion, Model-Glue, Hibernate, Spring, and Groovy - Ray Camden
- AIR: Building Desktop Applications with Flex 3 - Rob Rusher
- Hack Proofing ColdFusion - Shlomy Gantz
Definitely not your father's CFUNITED!
We've been using Hibernate for a while to manage the persistence of our business objects and that has driven us toward Groovy for those business objects since, right now, you cannot use Hibernate with ColdFusion Components in any meaningful way and we didn't want to write all the verbose code that Java would require.
A recent discussion on the Transfer mailing list has focused on how to model one-to-one relationships and why Transfer doesn't support them directly. This is one area in which Hibernate's features go far beyond what Transfer offers - and part of the reason for us to "switch" to Hibernate for future product development.
Another modeling quandary when it comes to persistence is how to handle inheritance.
We use Transfer for all our persistent business objects and almost all of our business objects have a decorator defined (for validation or some additional business logic). We also use Brian's TDOBeanInjectorObserver to automatically inject services into our business objects - just add a setter for a service and the bean injector takes care of the rest.
Here's the bean injector definition in our ColdSpring file:
<constructor-arg name="transfer"><ref bean="transfer" /></constructor-arg>
<constructor-arg name="suffixList"><value>service,datasource</value></constructor-arg>
<constructor-arg name="debugMode"><value>true</value></constructor-arg>
</bean>
So how do we add the on-demand encryption to our business object's data?
For ease of reference I just wanted to post a quick note highlighting Bob Silverberg's excellent series on using Transfer ORM and OO architecture:
- How I Use Transfer - Part I - Introduction
- How I Use Transfer - Part II - Model Architecture
- How I Use Transfer - Part III - Abstract Objects
- How I Use Transfer - Part IV - My Abstract Service Object
- How I Use Transfer - Part V - A Concrete Service Object
- How I Use Transfer - Part V.1 - A Comment and Response
- How I Use Transfer - Part VI - My Abstract Gateway Object
- How I Use Transfer - Part VII - A Concrete Gateway Object
There's a lot of good information here although it may be a bit overwhelming on first read so be prepared to keep coming back to these posts!
The platform is powered by ColdFusion 8.0.1, running Model-Glue 2 (BER), ColdSpring 1.2 BER and Transfer 1.0 BER and runs on a cluster of 64-bit Linux servers with synchronization of the Transfer cache managed through a set of ActiveMQ JMS server instances. The database is MySQL.
We're continuing to work on a stream of minor releases as well as planning our next major release with a lot of new features.
I'll be pulling together some presentations and articles about how we're using the frameworks together (because I think we're doing some interesting and unusual things, behind the scenes). I'll some a few snippets of code at Scotch in both of my presentations (and again at CFUNITED). I'm talking to the CFDevcon organizers about appropriate topics and they suggested a framework-related session so that might be the first unveiling of some of our ColdSpring / Transfer tricks and tips. I expect Ray will also be blogging about some aspects of our application architecture in due course.
Feel free to sign up for a free Broadchoice account and see what we've been up to! Our support site (also powered by the Broadchoice Web Platform, as is our public website - we like to eat our own dog food!) has several ways for you to provide feedback to us!
As many of you know - especially those who attended cf.Objective() - we hired Alagad to have Mike Brunt perform extensive load testing on our system, built with Model-Glue 2, ColdSpring and Transfer. One of the issues he found was a memory leak and Mark worked tirelessly with us prior to, throughout and after cf.Objective() to track it down and fix it. Mike helped us tune and debug our systems and I look forward to having him back to help us when we move to the next stage with our servers (as we investigate additional instances and more JVM tuning).
So download and test Transfer 1.0 RC 2 and report any issues you find to Mark so that he can fix them in time for the 1.0 release at WebDU!
Here's what I said in response:
Transfer continues to evolve at a rapid pace. The SVN repository is light years ahead of the last "official" release (0.6.3) and 1.0 is "coming soon". Mark Mandel deserves huge kudos for his work on this project - and his intent to turn this into "Professional Open Source". In other words, making Transfer something we can rely on like we rely on JBoss or MySQL today.
Come to cf.Objective() 2008 to hear Mark talk in person about Transfer in two great sessions!
I know Brian Ghidinelli also ran into this issue and had started to write his own as well. I expect he'll switch to Brian Kotek's version now.
So what does it do? Well, as you build complex domain objects by writing decorators for Transfer objects, you find you need access to services that you are managing with ColdSpring. Transfer provides an event model so you can add a listener (observer) for the afterNew event and use that to inject dependencies into your domain objects. It's a fairly manual process. What Brian's CFC does is completely automate the process. You declare the injector in your ColdSpring file and pass in Transfer to its constructor. When ColdSpring initializes the injector, the injector registers itself as an observer for that event and then it automatically injects any matching services, based on setters in the decorator. Very slick!
The exception from Reactor - about the database mismatch - is swallowed by ColdSpring and reported as a bean initialization exception. If that in turn is caught and dealt with by the application's initialization code, you no longer have any indication of what the problem might be.
Fortunately, we have a great tool at our disposal to dig into these buried exceptions: the ColdFusion 8 debugger!
I fired up the debugger in Eclipse and enabled break on CFML exceptions and write exceptions to the Eclipse log. As expected with ColdSpring and its exception-happy nature, I hit a few false starts but then I hit the Reactor exceptions. There are a couple of harmless exceptions as it tests for some optional plugin points but then you get the real exception, showing up in the Eclipse log. Very convenient!
Next up I covered the Fusebox 5.5 release which is currently in limited Alpha with a public Beta planned in July (as soon as we can get enough documentation together on the new features). I also announced publicly that providing a migration path for Fusebox 3 was on the roadmap (for Fusebox 5.7 probably).
Matt Woodward (and Peter Farrell) presented Mach II 1.5 which is in Beta right now, and the new website. He also talked about plans for their 2.0 release (but didn't go into specifics).
Next up was Chris Scott, who said that an official 1.2 release would appear within a few weeks and then they would be working toward a 1.5 release. This will be the last release of ColdSpring that will run on CFMX 7 - ColdSpring 2.0 will require CF8 because they want to take advantage of cfinterface and onMissingMethod() to make ColdSpring faster (and simplify the core files).
Last up was Doug Hughes who assured us that Reactor would hit an official 1.0 release as soon as the documentation was complete. Ah, the dreaded documentation...
Brian shows how to use the factory-bean and factory-method descriptors in your ColdSpring XML file to handle automatic injection of beans obtained from factory objects that are also managed by ColdSpring. It's a powerful technique that helps completely remove a number of dependencies from your application code.
I use the same technique with Transfer and ColdSpring:
<bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
Andrew Shebanow has a good post about this tradeoff in relation to Twitter, which is powered by Ruby on Rails. Alex Payne, of Twitter, has talked about their experience with the massive scalability explosion that the site has seen and some of the "friction" that they've encountered trying to refactor the abstractions out.
I worry about this in the context of most every framework I use - and there's certainly been plenty of noise on mailing lists and blogs over the years about the performance of frameworks. One of the reasons I've settled on Fusebox for the Scazu website is that it compiles all the circuit files down to inline CFML on the first request so the abstraction is compiled out (this is one of the core messages in my "Extending the language of Fusebox" presentation - see slides and sample code under 'SOFTWARE' on my blog).
I've also been a fan of Transfer for ORM, lately because of the TQL - Transfer Query Language - component that lets me write complex database queries using an object / property abstraction. It looks much like SQL but lets you use the terminology of your application instead of the terminology of the database. It does add overhead but refactoring methods that use TQL into methods that use the underlying raw SQL is pretty straightforward and can be done on a method by method basis (and, in fact, I recently refactored one performance-critical method in exactly this way).
Going back to Andrew's post (and the underlying comments from Alex), I'd say that Transfer has a low friction coefficient that contributes to why I like it so much.
For the first role, we want someone who can create crisp, clean user interfaces that are intuitive and easy to use. You'll have good graphic design skills and the ability to take a UI vision and turn it into lightweight HTML + CSS, with slick JavaScript for interactivity. If you've got ColdFusion skills, that's a bonus.
For the second role, we want someone who can build high-performance, highly scalable ColdFusion systems. You'll have a good grasp of application frameworks and object-oriented development (preferably Fusebox + ColdSpring + Transfer since that's what we use - but experience with otherwise frameworks and a willingness to learn will count).
For both roles, you will be able to work independently (and remotely, if appropriate) but with a focus on delivery and collaboration. Familiarity with source code control (e.g., SVN) and bug tracking software (e.g., Trac) is a requirement.
It's early days for Scazu so we're looking for folks who can be creative about compensation in exchange for a stake in the company. Our collective success will mean you'll be in at the ground level and able to build your own teams over time so it's a great opportunity for the right folks!
Reactor:
- Reactor implements the Active Record pattern, with objects knowing how to handle their own persistence.
- Reactor provides a rich OO-style query expression mechanism (you construct queries as OO data structures, then have Reactor execute them).
- Reactor can deduce the basic structure of your database tables for you - you only need XML to describe relationships or to alias columns and tables.
- Reactor generates "record" objects, gateways and metadata.
- To customize a Reactor object, you extend the generated object (and Reactor does not overwrite it).
- Reactor does not provide caching.
Transfer:
- Transfer focuses on business objects and provides a data access layer - you ask Transfer to load an object, you ask Transfer to save an object.
- Transfer provides a SQL-like abstraction, called TQL (Transfer Query Language), that makes handling queries of related objects really easy.
- Transfer does not introspect the database - you need to specify all of the table structure and relationships in XML, but you can also organize those object mappings into packages and have plenty of control over how the relationships are represented in the object model.
- Transfer builds your business objects on the fly, rather than laying down CFCs for you.
- To customize a Transfer object, you can either write CFML functions directly in the XML (and Transfer will include those when it generates the business objects on the fly) or you can write a "decorator" object to wrap any Transfer-managed business object.
- Transfer provides a sophisticated caching layer.
The two key new features in 0.6.3 are:
- afterNew listener hook so you can inject dependencies into newly created TransferObjects
- TQL - Transfer Query Language, modeled on Hibernate's HQL
You'll learn how we built the back end that supports several functions behind Acrobat Connect and Adobe Document Center - and Kuler - as well as some of our pain points and, in particular, the problems that arise when dealing with error handling around the boundaries of systems in a Service-Oriented Architecture.
I created a test case for parent / child bean factories and aliases, I put nothing but aliases in the child factories. ColdSpring couldn't find the aliases. I deduced (wrongly) that ColdSpring did not allow aliases in child factories to redefine beans in the parent factory.
Silly me. Turns out that my test case uncovered a bug in ColdSpring: aliases only work if the factory contains at least one real bean. At least, that's what my test cases seem to show. I'm going to run this past the ColdSpring crew for confirmation.
Want to share a single instance of Transfer between multiple Model-Glue applications?
Original code / post edited out.
The definition of transferFactory is in your parent bean factory which you tell Model-Glue about by putting this line in your index.cfm:
All the Model-Glue apps share that Application.cfc and so they all have the same parent bean factory which contains the single instance of the Transfer factory object. Each Model-Glue app has its own proxy object that delegates an alias that refers to the single factory object.
The code needs some cleanup but I figured it might be nice to get this out in front of people to get some feedback. This will eventually be the solution to ticket 25 as part of Fusebox 6.
Also in the repository are the Reactor and Transfer ORM lexicons that I showed at the Frameworks Conference. Again, these need code cleanup and enhancement but they should be a good starting point for people wanting to use either ORM library with Fusebox. A more comprehensive Reactor lexicon is also available.
The code includes:
- The ORM lexicons which provide identical syntax for accessing both Reactor and Transfer within your circuit.xml file.
- The Cat Club task manager variant that is, essentially, the Model-Glue version (ggcc7) implemented as a Fusebox 5 lexicon.
- The simplified task manager application that uses the ORM lexicons.
- The new and as yet unannounced assertions lexicon / plugin that implements preconditions, postconditions and invariants.
In addition to the documentation enhancements I mentioned earlier, the official 0.6.1 release includes improvements to the caching engine and a variety of bug fixes and performance enhancements. It also removes the earlier Model-Glue integration files because support for Transfer has now been added directly into Model-Glue itself.
Joe mentioned that he has moved the explicit Reactor ORM support declarations out of the core Configuration.xml file. In that post he said you could just declare the ormAdapter and ormService beans directly yourself.
His code base has moved on (already). If you download the very latest from SVN and you have the very latest ColdSpring from CVS, then you can take advantage of the new <alias> tag in ColdSpring!
In your Model-Glue application's ColdSpring.xml file, just add these two lines to use Transfer:
<alias name="ormAdapter.Transfer" alias="ormAdapter"/>
<alias name="ormAdapter.Reactor" alias="ormAdapter"/>
Still concerned about Transfer not being at "1.0" yet? Transfer 0.6 powers some of the machinery behind the recently released Adobe Acrobat Connect and Adobe Document Center services.
You can read all about the new features in his blog entry about the new release. If you have any concerns about trying Transfer because it hasn't reached the magic "1.0" release yet, put those concerns aside and try it anyway. My team has been using Transfer happily for a while (and will shortly be launching a new service where the persistence layer is powered by Transfer!).
As someone who has contributed extensively to Mach II, Model-Glue, Fusebox, ColdSpring and Reactor (phew!), I would like to step up and defend Joe's decisions - he's done a sterling job, sticking to his vision for the framework and has been very clear about what should be in the framework and what should not. As a framework developer myself, I can tell you it's a rocky road. The "community" deluge you with requests for all sorts of features and you have to stand firm and defend your vision. None of the popular frameworks are "kitchen sink" efforts - there are countless feature requests that have been denied by the framework authors.
I've requested enhancements to all these frameworks. Some of those requests have been implemented but most have been denied. Even as lead developer on one of the Mach II releases, some of my suggested enhancements were turned down (and some of the changed Peter implemented in Mach II were reverted as inappropriate for the framework).
When I built Fusebox 5, I was deliberately very conservative about what went into the framework and what didn't. I implemented a few things the community really wanted that I didn't think were great ideas but I also did not implement several things that I thought were great ideas that the community weren't very interested in.
Fusebox 5.1 will be a fairly conservative enhancement release. Fusebox 6 has more scope for adding features but, even so, backward compatibility will be maintained and the addition of features will be strictly controlled but community-driven.
I don't know how community-driven Mach II is. I don't think it has a public bug tracker (Model-Glue, Fusebox, ColdSpring and Reactor all do). I get the impression that Application.cfc support was added for coolness (the other frameworks have taken great pains to remain compatible with CFMX 6.1 and equivalent competing ColdFusion engines).
Third, crack open Model-Glue and edit one of the core files - shock! horror! Edit ModelGlue/unity/config/Configuration.xml and change the definitions for ormAdapter and ormService as follows:
This defines what ORM service to use, such as Transfer.
-->
<bean id="ormAdapter" class="transfer.modelglue.TransferAdapter">
<constructor-arg name="framework">
<ref bean="ModelGlue" />
</constructor-arg>
</bean>
<!--
This defines the ORM service
-->
<bean id="ormService" class="transfer.TransferFactory">
<constructor-arg name="configuration">
<ref bean="transferConfiguration" />
</constructor-arg>
</bean>
Your Transfer configuration might look like this:
<constructor-arg name="datasourcePath">
<value>/environment/transfer/datasource.xml</value>
</constructor-arg>
<constructor-arg name="configPath">
<value>/environment/transfer/transfer.xml</value>
</constructor-arg>
<constructor-arg name="definitionPath">
<value>/environment/transferdata</value>
</constructor-arg>
</bean>
Joe and Mark and I are still looking at what it will take to support relationships with Transfer in Model-Glue. To be clear, Transfer has sophisticated support for relationships but Model-Glue's ORM support is very Reactor-centric right now which makes this level of integration somewhat more difficult!
I've talked before about the structure of our service-based system: we use ColdSpring to manage all of the model components; we have simple web service / Flash Remoting facades in front of that model; we share that configuration with our Model-Glue applications using the parent bean factory feature of ColdSpring. I've been focusing on the Model-Glue side of the things this week, so I thought I'd blog about how that's working out for us.
He also listed some of the enhancements he is planning on adding shortly, including a separate configuration object which will make Model-Glue integration easier.
Model-Glue has an adapter-based architecture for ORM and ships with an implementation for Reactor. It has an ORM controller that implements the "Generic Data Messages" (e.g., ModelGlue.genericList) and relies on a Reactor-specific adapter for the actual integration with the ORM.
The ReactorORMController that ships with Model-Glue is almost entirely generic because of the adapter interface which is a testament to Joe's great design skills. Because of that, I was able to get a basic Transfer adapter up and running fairly quickly to the point that the GDMs all worked. A bit more work and I was able to get basic scaffolding working too (by creating some basic metadata and a fake dictionary object).
I haven't tackled any of the table-to-table relationship stuff but I wanted to let a broader audience know about this proof of concept to gauge interest...
Fast forward to today and Mark just committed some changes to CVS that provide a number of documentation improvements as well as the ability to turn off the internal transactions in Transfer's create / update / delete operations so that you can wrap multiple Transfer operations in your own cftransaction tags (just as Reactor allows). He has also recently added a readByPropertyMap() method that allows objects to be read based on a collection of property values (again, much like Reactor's load() method).
Reactor was the most ambitious and seems to have attracted all the attention. The other two got a bit lost in Reactor's shadow.
I recently started taking a second look at the Transfer ORM library by Mark Mandel and it's really grown up a lot since I last looked at it.
It takes a different approach to Reactor (and Arf!), not relying on the ActiveRecord idiom, but instead assuming intelligent business objects for which Transfer provides persistence. It may seem a subtle difference but it's an important one. A business object does not know how to create / read / update / delete itself - that's the job of Transfer itself - but Transfer provides the infrastructure to create and manage the business objects (as well as persist them). Transfer provides a solid event model (before / after observers for persistence operations) and has recently added full decorator support so you can write easily customizable business object CFCs that are "attached" to a persistable CFC that handles the core business object data.
Transfer also provides very sophisticated caching machinery (which I haven't fully gotten into yet).
Installation is clean and straightforward. Although Java code is used, no special attention needs to be paid to the JAR it needs because Transfer has its own class loader built in to take care of the JAR. Very slick!
It also seems to be a very low overhead ORM, in terms of raw performance.
Currently supports SQL Server, MySQL, Oracle and PostgreSQL but the approach it takes is much more generic than Reactor so adding additional databasess should be very easy. It doesn't use database introspection so there's less "magic" going on (which probably also contributes to the lower overhead in performance).
Definitely worth another look. If you don't like the ActiveRecord style of persistence, Transfer might be just what you're looking for.
Oh, and Mark Mandel created a #transfer IRC channel on DAL.net so if you have questions, pop in and he'll be there (during the Australian day time). I'll be there too.
Everything's coming together nicely for CFUNITED in terms of framework releases. ColdSpring is at 1.0 RC 1, Reactor is now at 1.0 BC 2, Model-Glue: Unity is in public beta and Fusebox 5 RC 1 will be announced at CFUNITED.
From the video, you'll see that Unity uses Reactor to drive the database access and ColdSpring to manage both the Model-Glue service components and the Reactor factory itself. A very powerful combination!
The benefits of ColdFusion are slowing becoming apparent to my (formerly non-ColdFusion) colleagues: Java integration is fairly seamless, exposing code through web services or Flash Remoting is almost trivial, high-level tags that simplify integration tasks.
Within my overall team, I run a small group focused on core infrastructure and integration and we're getting into some prototyping work now and for that we're going to use Model-Glue to build quick HTML user interfaces and test harnesses and Reactor to simplify the data access layer, with a view to moving to custom SQL queries for performance later on. Given the ease of using ColdSpring with Model-Glue to manage the service layer, I strongly suspect we'll end up using all three frameworks together for our prototyping.
Over time we'll build out Flex 2 user interfaces, probably even for the internal administrative consoles that we might initially build using Model-Glue. The service layer we build should be directly reusable in that situation.
So far, the initial response to Model-Glue, ColdSpring and Reactor has been positive and we're looking forward to Model-Glue 2.0 a.k.a. "Unity".
<constructor-arg name="configuration">
<value>/ggcc10/config/reactor.xml</value>
</constructor-arg>
</bean>
<bean id="taskGateway" factory-bean="reactorFactory"
factory-method="createGateway">
<constructor-arg name="objectAlias">
<value>task</value>
</constructor-arg>
</bean>
In the ModelGlue.xml file, specify the ColdSpring loader and that you want autowiring:
value="ModelGlue.Core.ColdSpringLoader" />
<setting name="autowireControllers" value="true" />
<cfargument name="gw" required="true" />
<cfset variables.taskGateway = arguments.gw />
</cffunction>
Magic!
Reactor is also getting some performance tweaks - some are already in the repository, more will be committed before cf.Objective() (do you see a pattern here?).
Check out the latest "BER" versions to try out the new abilities!
ColdSpring has recently been enhanced to support factory-bean and factory-method attributes in a bean definition so that you can declare beans that are actually created by external factories such as Reactor.
You declare the Reactor factory as a bean (managed by ColdSpring). Then you can declare DAO and gateway objects, in the ColdSpring XML file, which are created by the Reactory factory bean.
It's very slick.
However, most Reactor operations are focused on record objects which are transient (not singletons) and whilst those could be managed by ColdSpring, generally only singletons - per-application objects like services - should be managed by ColdSpring (since it adds a layer of complexity to object creation in order to leverage the power of the framework).
I was attempting to apply AOP to Reactor-generated objects so that I could simulate the security model in ggcc9 (the ninth variant of my frameworks comparison code - see the software pod on my blog). ColdSpring assumes that AOP-controlled objects are simple concrete classes and therefore it doesn't work with the nested inheritance hierarchy of the Reactor-generated DAO and gateway objects.
I did eventually make it work but it requires changes to ColdSpring and, for some of the autowiring I was doing, some changes to Model-Glue as well. I've submitted the patches to the revelant mailing lists for consideration but it means that I can't realistically release the ggcc10 variant's source code.
Despite my minor frustrations, the combination of the three frameworks is immensely powerful. If you look at the ggcc9 variant, you'll see it has several model CFCs and several bean CFCs. The ggcc10 variant has none of that. The model was really just DAOs and gateways which Reactor provides automatically and the beans become records which, again, Reactor manages automatically. About a dozen CFCs went away in the conversion. That's a lot of code I wouldn't have to write!
He credits me with donating MySQL 4.x support but I should point out it was only partial support and several other Reactor community members have since contributed bug fixes and enhancements for that. Oracle support is also in the works from the Reactor community (but is trickier due to how it handles auto-incrementing primary keys - or rather how it doesn't handle them but relies on sequences etc).
Steven Erat has a good write-up and link to the recording.
Doug will be presenting Reactor at the Online ColdFusion Meetup Group on January 19th (36 yes, 7 maybe). He'll also be presenting Alagad Image Component and Alagad Captcha the week before, January 12th (32 yes, 7 maybe). RSVP soon!
That means you can't have columns called: isbuilt, recordfactory, datasource, tablename, primarykey, relations, term or uilibrary.
I doubt you'll trip over those but I did - I'm building a wiki sample app and had a column called... term. I renamed it to wikiterm and all is well.
On January 19, he will talk about Reactor.
See the Online ColdFusion Meetup Group calendar for more details.
The article related Franz Garsombke's experiences refactoring a large, poorly-structured Java application using five iterations. Along the way, explaining the benefits of layered architecture, dependency injection, AOP, mock objects (for testing), DAOs / Hibernate and Spring, he talks about creating a framework for mapping JavaBeans across application layers / interfaces. They made it available as an open source project called Dozer. It's an interesting idea and I can see its applicability in the ColdFusion world as well, especially now we are starting to see the use of dependency injection (ColdSpring) and ORM libraries (like Arf!, Transfer, Reactor, objectBreeze etc).
However, when I went and looked at the documentation, the XML (which describes the mapping between objects) struck me as being pretty ugly (tags called classA, classB, A and B for example). I'd be interested to hear whether anyone has used Dozer and / or whether anyone has created something similar for ColdFusion...
Naturally it's cat-related. I built the On Safari website last year (I am not responsible for the visual design!) using Fusebox and what little dynamic content I needed I was loading into the database from CSV files.
This year I decided to build a small content admin section to make this a more pleasant process as I add more content that people send me about past On Safari shows. For the admin, I decided to use Model-Glue, ColdSpring and Arf!
Code to fetch all the awards and sort them by year and sequence number:
<cfset var awardSelect = award.list(orderby="osyear DESC, seq ASC").getQuery() />
<cfset arguments.event.setValue("awards",awardSelect) />
Code to read in a single award record by ID:
<cfset var award = variable.rf.createRecord("admin.model.cataward") />
<cfif len(awardId)>
<cfset award.read(awardId) />
</cfif>
<cfset arguments.event.setValue("award",award) />
Code to save an award (either new or edited - Arf! knows that a blank ID means add a new record):
<cfset var award = variable.rf.createRecord("admin.model.cataward") />
<cfset var i = "" />
<cfloop list="#fields#" index="i">
<cfinvoke component="#award#" method="set#i#">
<cfinvokeargument name="#i#" value="#arguments.event.getValue(i,'')#" />
</cfinvoke>
</cfloop>
<cfset award.save() />
<cfset arguments.event.addResult("success") />
And, finally, code to delete an award:
<cfset var award = variable.rf.createRecord("admin.model.cataward") />
<cfset award.setId(awardId) />
<cfset award.delete() />
<cfset arguments.event.addResult("success") />
You can decide for yourself how much work this saves. The cataward CFC is essentially empty (just the Arf! template). It was certainly nice not having to write CRUD SQL but in an app this size there wouldn't be very much of it.
It works, it's reasonably fast - certainly fast enough for my little admin needs.
What could I do better? Well, I ought to have a separate DAO layer and isolate the use of Arf! inside that rather than just manipulating Arf! in my controller but I just wanted a quick'n'dirty content admin. I'm only using ColdSpring to create and load the Arf! Datasource and RecordFactory objects (overkill but convenient). I'm using the autowire feature (in the ColdSpring CVS version, not yet released).
For the most part that's fine - I just put the details into a config bean and have all my code read it back out.
Over the last couple of days I've built an Arf! / ColdSpring / Model-Glue application to manage content (actually on cat-related site, not mine) and whilst I had everything working just fine locally (Arf! is pretty slick for simple persistence management), nothing would work correctly on the shared host.
Arf! generates code that assumes the username / password are held in the datasource definition so the first thing I had to change was the <cgquery> tags in the generator (yes, cg, not cf). That still didn't work tho' because Arf! takes advantage of the datasource service in ColdFusion MX to access the database metadata. It turns out that the getConnection() call can be passed the username / password - and that is needed in this case.
So I modified the Datasource.cfc bean to have a username / password and modified the factory code to use those attributes in the getConnection() call.
Then I started to get connection errors. It turns out that with some database systems, you cannot close the connection until after you have finished accessing the metadata. Joe has a comment in his code (for the column metadata) that indicates he'd hit a similar problem. I moved the call to loadTables() into loadDatabaseMetadata() (where the connection is still active) and I moved another call to conn.close() after the last reference to the metadata (where the column metadata is fetched).
Then I discovered the CFC name used to represent the table is case-sensitive on some systems so I renamed my CFCs and edited the code that creates them.
Finally, it all worked. Well, almost. After all that, I'd forgotten that this particular site is hosted on CFMX 6.1 rather than CFMX 7 - so now I need to rewrite my <cfform> tags not to use format="xml". How quickly you get used to new features!
Overall, the Arf! functionality is very useful and saves me writing a lot of tedious SQL. I still have to write tedious data entry forms and marshal data out of the form (event object, in this case) into the 'record', but it definitely saved me some time.


