|By Simon Horwith||
|October 20, 2004 12:00 AM EDT||
Beginning with the release of ColdFusion MX, the ColdFusion Application Server now runs on top of an underlying Java engine. Java, as we all know, is an object-oriented language. However, the question of whether CFML is an object-oriented language has been under debate since the ColdFusion Component (CFC) Framework was introduced in ColdFusion MX.
Too much energy has been spent bantering about whether CFML is object oriented, rather than exploring uses for the object-oriented features that CFCs brought to the language. Among these features were inheritance, support for the creation of Web services, the ability to encapsulate data and business logic in objects and to perform instance-based development, and metadata.
So many new features mean that the rules have changed for ColdFusion developers. We must now relearn and redefine best practices, which not only means changing the way we write code but also the way that we plan our applications. Fortunately, we can take advantage of a wealth of knowledge about what does and doesn't work. We can do this by studying the design patterns and development techniques that have been tried and tested by object-oriented developers, most notably Java developers, for more than a decade. Because of the very nature of Web-application development, J2EE design patterns are particularly applicable when architecting applications with ColdFusion.
Unfortunately, understanding design patterns is only the first step toward building CFML applications the right way. There still remains the challenge of not only selecting the right pattern for the job, but also knowing how to implement a pattern (or not) in your code.
No single article, or book for that matter, could ever hope to provide the magic answer to determining what pattern to use when, or even how to properly implement any one design pattern in CFML. There are some patterns that lend themselves to the language very well and others that do not, and most could be implemented in any one of several manners. It would take several months, or years even, to properly test and document it all. The purpose of this article is to shed some light on a general realm of design patterns. This article will also case study a recent implementation that allowed me to quickly help reduce overhead, boost performance, and add functionality to a client application. It is just one example of how knowledge of design patterns and creativity with CFML can allow you to achieve your goals quickly and efficiently.
Working as a consultant, I am often asked to advise on architectural decisions as well as troubleshoot existing architectures. A client of mine developed an application (a framework) with ColdFusion allowing rather inexperienced developers to quickly and easily create data entry and data display screens on the Web. The idea being that defining the database table with XML and adding a few lines of code to a display file and a CFC (which inherits from a parent "object" code base) is all it takes to create screens that have a uniform look and feel and reproduce the screens in a massive legacy application. The client hopes to move the entire legacy application onto the Web within two years.
My role for the past year has primarily been to assess risks, advise on the best course of action, and to come in and fix problems when all else has failed. As you can imagine, at any given moment I might be researching several issues at once. Just prior to a recent major release of the software, I was asked to look into two major issues. One was the replication of sessions when servers in our J2EE clusters failover and the other was application performance in general.
The first thing I did was tackle the session replication problem. As it turns out, this was right around the time that J-Run Updater 4 and the ColdFusion MX 6.1 Updater were both released to the public. Unfortunately even with both updaters installed, after all of my testing I was unable to consistently make ColdFusion session variables replicate to a server that was added to the cluster. Session replication and failover works pretty consistently - when you turn off a server everything fails over and replicates fine. However, when you add a server back into the cluster, the session doesn't always replicate onto the new server properly. The application server console always reports a J2EESessionScope error when a server rejoins the cluster. Sometimes ignoring this message appears to be okay, and sometimes it does not. It's also worth noting that sending requests while a server was in the process of rejoining the cluster seems to encourage session replication failure. All of my tests with "pure" Java, even simple JSP files, consistently worked. Sessions always replicated just fine. One of my rules of thumb is to never spend too much time on one error, so I put that problem on the back burner and focused on the other problem: performance. We'll revisit the question of session replication later.
There are a lot of things that can kill performance and a lot of things that can be done to speed things up in an application. Every developer has a mental list of the things they do on a page or in an application to make it efficient (use CFSCRIPT, avoid unnecessary looping/conditional logic/HTTP, cache things in memory, optimize and minimize database calls, etc.). The list can be huge but the last two points, taking advantage of the server's memory and minimizing/optimizing calls to the database, are the two most common ways developers speed up their applications. But what can you do to speed up an application that already has all of these things in place? Go line by line through the code looking for small tweaks here and there?
You could do that, but the performance gains would be minimal and it would take a long time in this case. The application I was working with already consisted of about 600 files including not only the framework, but also the screen definitions and accompanying logic as well. The application I was asked to optimize also already had a great deal of caching and code reuse in place, and all of the database access had been gone over with a fine-tooth comb. When faced with a situation like this, I find that it's a good strategy to take a step back and look at the big picture.
Looking at the entire application from a distance must be a lot like looking at Earth from outer space. There are no countries, no borders, and no politics. There's just water, land, and clouds all working together to create that delicate balance needed to sustain life. All of the minute details blur together, allowing you to concentrate on the system as a whole rather than trying to focus on all of the smaller parts, which in the big picture are insignificant. When I thought about how I would model this client's application and how all of the parts of the model interacted, I looked for problems. If I didn't find any, I'd take a step back. Eventually, a potential problem almost always presents itself. In this case, when I finally got to the point of blurring modules into single tiers that interact with one other, something dawned on me: tier communication patterns.
It had been some time since I'd thought about all of the design patterns I've studied in the past. But I remembered that there are a few patterns I'd read about that deal primarily with communication between tiers in an application, whether those tiers were the presentation and business logic tiers or business logic and database. The application I was optimizing had a terribly high number of CFC instances being passed around between the presentation layer and the business components, between Java and ColdFusion, and between business components and other business components. I hadn't thought about this as a problem because the individual CFC instantiations and method calls were taking very little time at all. Most of the CFCs were instantiated and cached in memory once and then reused throughout the application. Still, there is a certain amount of overhead in passing instances of complex objects around in memory as well as in persisting all of the instruction sets for dozens of components. Many of these were even cached in sessions (for justifiable reasons). A few patterns came to mind that might shed light on a possible solution: Data Transfer Objects, Lazy Load Pattern, and the IsDirty Pattern. So I went back to the books and brushed up on the intricacies of these three patterns and some of their related patterns.
After refreshing my memory about these patterns, it became clear that what I was looking for was something along the lines of the Data Transfer Hash Pattern (the other patterns I mentioned have qualities I'm looking for but are more specific to database interaction). The idea behind the Data Transfer Hash Pattern is that passing instances of objects around carries with it a lot of overhead, so hashmap representations are used instead. Think of a hashmap as the Java equivalent of a ColdFusion structure. It's much more lightweight moving a hashmap data structure around rather than a complex object that contains functionality (e.g., a CFC instance). The one downside is that there's no easy way to enforce the values within that hashmap. This is a problem more easily remedied in Java by using Objects, but all too familiar and not-so-easily remedied in ColdFusion, as our only option is to use a CFC with a constructor. The IsDirty Pattern, in a nutshell, is all about setting a Boolean flag in a data container so that database commands to manipulate data are executed only when data has actually changed. The Lazy Load Pattern is similar to IsDirty in that it reduces database connectivity overhead. However, it does so by not retrieving data until it's specifically requested. In implementing the Data Transfer Hash Pattern, I will also borrow from these ideas in that I will keep external object access to a minimum until it is required. But the solution is still in essence more of a Data Transfer Hash Pattern solution.
So how do I put this idea of only transferring simple data collections rather than object instances into practice? Ideally, I'd like a way to represent the data contained within a CFC instance as a structure. Furthermore, I want to do so in a way that's flexible, easy for the end users of the framework to implement, and doesn't break the existing code base. Fortunately, there are some features in CFML that I can take advantage of to make this work.
For one thing, we have inheritance. In my particular scenario I could easily modify component.cfc to offer this functionality within all CFCs. I described this technique in an article titled "component.cfc: The Mother of All Components" in the November 2003 issue of ColdFusion Developer's Journal. The code has to be very portable, which means that I don't want to rely on this technique. Fortunately for me, because my "fix" must be implemented in the context of a framework and all CFCs in that framework inherit from a "master" CFC, I can easily create a component and specify that the "parent" inherits from it (via the <CFCOMPONENT> tag "extends" attribute). It also occurred to me that until an elegant solution to my session replication woes is found, this pattern offers the added bonus of helping to address that problem as well. In ColdFusion MX (6.1 or prior) CFC instances are not serializable. This means that they cannot be replicated across servers in a cluster. I knew this and did advise my client that all CFC instances in the session scope had to be made nonpersistent. This resulted in two code bases. One we wanted to implement but couldn't until serialized CFC instances is supported (a feature rumored to be in Blackstone). The other one works around the issue by not keeping CFC instances in the session scope. If I could present a way to represent CFC instances as structures, not only would it reduce overhead, but it would also offer a means to persist CFCs and have them replicate. This could be accomplished by serializing and deserializing them as structures. Although I was having trouble making serialization work for ColdFusion, structures map to hashmaps (and vice versa) quite well. Plus I'd already proven that Java session information serializes just fine.
My solution took advantage of the metadata created for CFCs: metadata exposed with the getMetaData() function. I created three methods. One simply accepts a CFC instance and returns its metadata in a more "friendly" format. Another accepts a structure and the name (including full package) of a component and returns an instance of that component populated with the structure data. The third method accepts a CFC instance and returns a structure containing all of its properties. How could I guarantee that I retrieved all of the properties?
My requirements were simple, and fortunately the framework coding standards that were already in place assured me that the following conditions already exist. When serializing the data in a CFC within a structure, I look for any property defined with a <CFPROPERTY> tag. If a method exists with the name "get" plus the property name, the code calls that method to get the value. If a property is defined that doesn't have a "getter," I copy it directly from the component's public "this" scope if it exists. When creating a component instance from a structure, I create an instance of the component and look for an init() method. If a nonprivate init() method exists and the structure contains keys with names of any and all required parameters, I pass the parameters to the init() method. After calling the init() method, if it exists, the code then loops over the structure that was passed and calls any method named "set" plus the structure key name (passing it the structure key value). If no "setter" exists but a public property with the same name as the current key name exists, the code sets a variable in the component's "this" scope with the name-value pair from the structure. It's probably not the most robust or flexible solution in the world, but it does serve my purpose.
The rest of the implementation details are just a matter of persisting structures whenever possible if an object isn't likely to receive much activity and serializing that structure as a CFC instance in the event that an actual object is required in a certain scenario. It's worth noting that in a system in which the CFC instances receive frequent method calls that manipulate their data, the overhead serializing and deserializing to and from structures carries more performance hit than simply keeping a component instance in memory. However, often in other scenarios you can free up a significant amount of memory and other resources by not keeping so many objects in memory. This may result in better performance (it did in my scenario).
More and more it is becoming evident that it's necessary to have at least a basic understanding of design patterns. Coupled with the features in ColdFusion MX, this opens up the doors to a whole new realm of development solutions using CFML. Any of you who are interested in seeing the code I used to implement this solution, may download it from www.sys-con.com/coldfusion/sourcec.cfm or from www.horwith.com/downloads/dto.zip.
I encourage you to not just examine my code but (more important) think about alternative approaches to problems and creative methods for implementing the design patterns that already exist and are just waiting to be explored by ColdFusion developers. It's also worth noting that the technique I've outlined is what J2EE application servers already do to serialize sessions in a cluster. The application server itself is a very good place to look for inspiration when trying to determine how best to meet business requirements in your applications.
- Where Are RIA Technologies Headed in 2008?
- The Next Programming Models, RIAs and Composite Applications
- AJAX World RIA Conference & Expo Kicks Off in New York City
- Constructing an Application with Flash Forms from the Ground Up
- Building a Zip Code Proximity Search with ColdFusion
- Personal Branding Checklist
- CFEclipse: The Developer's IDE, Eclipse For ColdFusion
- Has the Technology Bounceback Begun?
- Cloud People: A Who's Who of Cloud Computing
- i-Technology Viewpoint: We Need Not More Frameworks, But Better Programmers
- Adobe Flex 2: Advanced DataGrid
- Web Services Using ColdFusion and Apache CXF