CFUnited 2009 Topics: Some Thoughts

Wednesday, December 24, 2008

Some of the topics for CFUnited 2009 were announced yesterday. I (Marc) am happy that my Automation session was accepted, though I'm more jazzed up about the "how to learn...." session that's currently languishing in the "maybe" column. Automation Session A quick note on the Automation session: I had an unbelievably fun time delivering the ANT presentation at CFUnited 2008 last year, but I'm not the kind of guy who can recycle a presentation every year. It ain't me. And thus the Automation session will NOT be a 60-minute session on ANT. It'll cover all kinds of nooks and crannies related to automation, from super duper braindead simple to code generation to who knows what. It's a Smorgasboard presentation. Disappointed... The one session I think should be a "must see" is Adam Haskell's "Red Green Refactor" presentation. I reviewed his slidedeck for this presentation, which saw its first iteration at BFlex/BFusion, and I have it on good authority that the presentation was outstanding. Adam is top notch and he knows his TDD stuff. I'm putting a campaign sign in my virtual Lawn right here: RedGreenRefactor CFUnited 2009. The other topic I'm quite interested in is the Mate Flex framework session. This isn't because I'm some framework whore. In fact, I won't touch a framework until I feel the pain that a framework purports to mitigate. This is why I haven't simply downloaded Mate myself and started playing. I don't know enough about Flex yet to know how it -- or any other framework -- can help me. But I want to know what solutions are out there when I do need them. I'm looking forward to seeing what Swiz has to offer, and I want to see how Mate compares or complements it. Lawn sign: Mate CFUnited 2009 Yet another is the Groovy/Flex presentation by Joe Rinehart and Barney Boisvert. I was fortunate enough to see Joe's Groovy/CF presentation at the BACFUG after MAX this year... it was kick ass! I now want to see how these dudes tie it into flex. But I wanna see some code man! Code code code code code. (and config config config config). I want to walk away from this session, go back to my room, download whatever sample code they provide, and be able to start knocking some stuff out. Tall order, gents. Lawn Sign: Groovy/Flex CFUnited 2009. With Code. And Lots of it. And another is the Facebook & CF session. All the hip kids are building apps on top of the facebook platform; and it seems like it'd be fun to spend a rainy Sunday dreaming up ways to make enough money building a facebook app such that I could a) buy a Maserati or b) afford Wharton for both my daughters. Lawn sign: Facebook and CF @ CFUnited 2009. Go vote! Excited about... It looks to me like Liz and crew have made a good effort this year to get rid of some of the presentation recycling that (in my lowly, 'umble, far-distant opinion) seems to have crept in. So there's lots of good new stuff. After a quick run through of the accepted topics, here's what caught my eye right away. This is "gut" stuff, without reading the session descriptions. CFMythBusters. I love watching Charlie Arehart speak. He knows his stuff inside and out, and he's a very engaging presenter. Swiz. At least one session related to BlazeDS At least one session that answers the question, "Why should you spend a gazillion dollars on this LCDS thingamajiggie?" Skinning Components in Gumbo Hacking your own website. (I want to see this so that I can go hack other people's websites and crash the interwebs). Actually, I want to see this b/c my company is currently seeking a particular ISO certification that requires internal security auditing, and I want to know the tips and tricks for busting our sites so that we can be prepared. Why no TDD presentations from us? The incomparable Sam Larbi mentioned on the twitters that he was surprised one of us MXUnit hombres wasn't presenting on unit testing at CFUnited this year. I don't want to speak for Bill on this matter, but I will say that I know Bill's interests of late are veering toward security testing, code coverage, and other higher-minded, less gruntish aspects of testing than unit testing. As for me, my reasoning goes something like this: I hate to suck. I mean, I really, really hate to suck. Now, 7 months after my complete clusterf**k of a presentation at webmaniacs, I still harbor regret and guilt. And here's one thing I know: a sizable part of my "success" as a presenter comes from my passion for the topic. And for me to be passionate about presenting something, it's got to teach me something. If I'm not learning anything during the creation of a presentation -- the slides, the concepts, the code, materials, etc -- then I'm bored with it. And I won't do that to people in the audience. If I'm bored, they'll be bored. And I'm getting too old to fake it. I can do it, but it requires more energy to phone it in than it would be to come up with something new. I was super stoked about Bill and my presentation at MAX this year b/c it was relatively new stuff for us. We hadn't presented on it before. And Bill's BACFUG presentation, while an "intro" presentation, took a completely different angle on the topic. But for me, I've lost passion for "introduction to unit testing". It's time to let other people who have the passion for the topic share their discoveries. To that end, I do hope that Tim Farrar's unit testing presentation gets accepted this year. Another Lawn Sign: Tim Farrar, Unit Testing, CFUnited 2009. Bottom line for me: if I'm giving a presentation, I have to learn something too. It's quite selfish, I'll grant that. But a man's gotta know what lights the fire, and I know that much about me. Fear not, gentle reader. MXUnit will be on the scene at cf.Objective this year with a brand new, code-and-what-am-i-thinking-as-i'm-doing-TDD-intensive presentation. It's a new one for me, a sort of "meta" presentation to try to get into the head of someone designing code for testability. Merry Christmas!

Speeding Up Eclipse

Thursday, December 18, 2008

Slooooooooooooow

So your Eclipse is sluggish, and you've tried the usual suspects: increase the heap and permgen, run -clean, close projects. You've found all the places where Aptana strangles your install and tried, to the degree possible, to tame its virus-like behavior. But something still feels off. Maybe startup is slow. Maybe, as happened to me, shutdown takes minutes. In my case, my eclipse install got to the point where shutdown was taking 2-3 minutes, and this is on a fairly performant machine. Finally, I had a Popeye moment: "That's all I can stands, I can't stands no more!". I opened up sysinternals excellent Process Monitor utility and watched what was happening. Bottom line: on shutdown eclipse was touching a gazillion different directories in my workspace, even directories on projects that were supposed to be closed. I do not know what it was doing (I suspect Mylyn, though, for what it's worth. But you'd have to rip Mylyn out of my cold, dead hands before I gave it up). So I decided to finally bite the bullet and adopt a practice suggested by many other people over the years (my buddy Mike used to yap at me all the time about this). It's a practice, however, that I tried before but couldn't commit to.

Separate Workspaces

What solved my problems: Separate workspaces. I know, you're thinking, "uh, duh, dumbass. that's what workspaces are for". But hear me out. When you start out with eclipse, you might have 5 or 10 projects in your workspace. A handful of java projects, some CF, some other play-around stuff... whatever. You wanna mess around with AIR, so you have some AIR projects. You have a handful of Flex projects. You decide one day to monkey with CFEclipse b/c you're so keen on giving back to the community, so you check out the 6 or so projects from SVN. You create a couple eclipse plugins yourself. Soon enough, you end up with 50 projects in your workspace. But you keep most of them closed, so technically they should be closed, right? "Yer dead to me", right? Apparently not. So I got my act together and finally separated out my different types of work into different workspaces, and the difference is dramatic. My primary workspace -- whittled down to a mere 31 projects -- starts up and shuts down in seconds. All the other workspaces, ranging from maybe 6 projects to 20 projects, load just as quickly. Nice thing is that now that I've got projects separated out, I don't need to keep projects closed all the time -- at least, not the ones in my "secondary" workspaces.

How to move projects from one workspace to another

But this post isn't just about my dummy AHA! moment. It's about a tip for a really fast way to move projects out of one workspace and into another. Here's what to do: To create a new workspace: Simply go to File -- Switch Workspaces and then type in a new Location. For example, for a workspace containing my Flex stuff, I just typed in c:\documents.....\mesher\EclipseWorkspaces\MyFlexWorkspace. Eclipse will then create a new, empty workspace. To get some of your existing projects into the new workspace:

  1. File -- Import
  2. General -- Existing Projects into Workspace
  3. Click "next"

Here's where it might get a bit tricky, so I'm going to go into all the detail for my own personal setup. I have projects scattered throughout my system, but largely they fall into two places: 1) my webroot where I keep all my cf projects, and 2) my actual workspace, where I keep most of my java projects. I'll start with my CF projects. Let's say I have 50 different projects underneath my webroot, and I wanted to break them into roughly a third. In the "Select Root Directory" dialog that comes up, I went to c:\inetpub\wwwroot. This brought up a ton of projects, only a third of which I wanted to move into my new workspace. It then scans for all projects and loads them into the window at the bottom of the panel. I clicked "deselect all", then selected just the projects I wanted to import. Then, I clicked Finish. That brought all the projects I wanted to import into my new workspace. NOTE: I did NOT click to "copy projects into workspace!". I repeated that step for the other projects I wanted to move into separate workspaces. Finally, I went into my primary workspace selected the projects that I had moved into separate workspaces, and deleted them. I did not select "remove from file system"! Remember... by moving projects to another workspace, you're not physically moving the location on the file system.

Preferences

One thing to note when creating new workspaces is that each workspace gets its own separate Preferences. This is a good thing! But it does create a bit of extra work if you simply want to replicate your preferences among the various workspaces. For me, the quickest way to move my preferences from the primary workspace from the secondary workspaces was this:

  1. in primary workspace, go to File -- Export
  2. filter on "preferences"
  3. Export all preferences to a file somewhere... I put mine on my desktop
Then, switch to another workspace.
  1. in this secondary workspace, go to File -- Import
  2. Filter on "preferences"
  3. Navigate to the file you created
  4. Click Finish
That's it! Do that for the other workspaces, and now your preferences are consistent. Got any other tips for speeding up Eclipse?

The Android Revolution

Tuesday, December 9, 2008

This is going to come across like bullshit hype, but I've never liked the fact that the hardware I buy (desktop, laptop, and phone, especially.) is effectively owned by the underlying operating system and carrier. Chances are I will never rebuild the OS or even write a killer-app, but it's getting old waiting and waiting and waiting until something gets fixed or a new version released or just trying to find a workaround to some feature I need. But with an open operating system for mobile devices (Google Android), developers and enterprising folks can provide products and services to otherwise enslaved users. Viva la revoluciĆ³n!
As a registered developer at http://market.android.com ($25) you can now purchase a fully unlocked HTC G1 for $399 (shipping is exorbitant if you're outside the U.S.). This and the Android SDK will allow you to do whatever you like with your phone. If you already have a G1, like me, you can wait until after the first 90 days into your T-Mobile account and they will give you an unlock code so that you can replace the SIM card with another carrier. If you don't want to wait, the enterprising folks at http://www.unlock-tmobileg1.com/, for $23, will do this for you. Here's a peek at some Android phone clones: http://clonedroidphone.com/ Android G1 Marketing crap: Android Dev Phone (May have to register first ...) Overview

Run and debug your Android™ applications directly on a device. Modify and rebuild the Android operating system, and flash it onto a phone. The Android Dev Phone 1 is carrier independent, and available for purchase by any developer registered with Android Market™. Price: $399 USD

Unlocked T-Mobile G1

How I Save Energy

Monday, December 8, 2008

Computers suck a lot of energy, as do the peripherals connected to them. Even when turned off, all devices draw at least some current. It's a trickle, but it adds up. First, the setup: I have a HP running Vista 64bit with two 19 inch monitors, a printer, speakers with subwoofer, cable modem, vonage router, and wireless router. The goal: When the PC shuts off, keep the cable modem, vonage, and wireless router still juiced, and completely kill the other peripherals... I don't want them drawing any power at all. Even better goal: not have to turn the PC off at all to make this happen (because PC startup takes a while). Even better: only a few minutes after I finish using the PC, have it shut down such that all peripherals are invisible to the grid, and with a mouse click, have everything back up within 10 seconds. No data loss, either, obviously. At home, here's how I contribute some treehuggin granola and lovin' to Mother Earth. Some time back, I heard of the Smart Strip power strip, a strip that enables a "controller" outlet to effectively cut off power to all other plugged-in devices when that controller itself is turned off. Seemed like a reasonable idea, so I bought two: one for my TV/devices and one for my computer. I'll focus on the computer with this post. I have the computer plugged into the control outlet. Monitors, printer, and speakers are plugged into the "controlled" outlets. Cable modem, vonage, and wireless router are plugged into the "always hot" outlets. Thus, when the PC is "off", the big-energy-sucking devices go dead while the ones I need to have on stay active. That's part 1. Some pics

This is all well and good... turn the PC off, and save some money and some trees. But that's not good enough. I want it to be brainless... I don't want to have to turn off anything, and I don't want to have to wait a few minutes for everything to boot back up. I want it to happen automatically, and have my entire "state" retained when it comes back up. So, on to Part II of how I'm saving the world. "Sleep" to the rescue. Now, back in the XP days, I remember sleep flat out sucked. It took forever to come back up, often longer than it would've taken to shut down and restart the PC. With Vista -- at least in my experience with this particular PC, sleep is quite good. I have my power options set to turn off the monitors after 5 minutes of inactivity and Sleep the computer after 10 minutes. Thus, 10 minutes after I walk away from the thing, my state is retained and everything "shuts down". All earth-melting peripherals are off the grid. Angels weep at my magnanimousness. Babes swooooon. Now, some latte-sipping hypermiler choad is saying "But that's 10 minutes! What about the Northern endangered spotted Newt! You're killing the Newt you paint-drinking techno-earthkiller!". OK, I'll grant that in those 10 minutes, multiplied by say 10 times a day or whatever, I'm not winning any Al Gore Humanitarian awards. So, on to Part III, for the Prius set. In Vista, you can control what the "power" button does. That's the little round thing with the bar up the middle. I have it set so that when I hit the power button, the computer goes to "sleep". This is not windows XP sleep. This is Vista sleep (again, at least on my PC). And it shuts down within about 5 seconds, and when I wake it back up with a single mouse click, it's ready to roll in about 5 seconds. Full network connectivity takes somewhere between 15 and 30 seconds to reestablish. I know it's back up when TweetDeck chirps. Now, I did hit a snag initially with keeping the router Hot. The PC would sleep, but then it'd come back to life a second or two later. I had to go into device manager, find the network controller, right click and select "Properties", go to the "Power Management" tab, and Deselect "Allow this device to wake the computer". Bottom line: for me, the Smart Strip + Vista + Power Options + Sleep-on-Power-Button combination has been a win.

MXUnit Eclipse Plugin Updated: A deal-breaker... broken!

Tuesday, December 2, 2008

I'm happy to announce another update to the MXUnit Eclipse plugin. This started out as an attempt to correct a deficiency that was a deal-breaker for some people, but turned into a few additional features as well. Here are the goodies. Any-resource-level CFC Path Property Several people who've tried to use the plugin have been stopped at the gates because the plugin could not correctly determine the dot-notation (CFC) path to a component. This was due to the way the plugin expected eclipse projects to be set up. When I first wrote the plugin, I knew there was one condition that would cause the plugin to not work for people... but I thought "Why the hell would anyone set up their environment that way?" I should've known... The proper fix is now in place: you can set the component root property at any resource level. This should eliminate any problems with the plugin determining the component root for a directory of tests since you can now specify that root on any folder, not just the project. In addition, there's a nifty feature contributed by a user named Alanyst that provides the "I don't want to use the webroot, but I want an 'empty' component root" behavior that some people have asked for. Method Timeouts Bob Silverberg, a dude I hold in high esteem, emailed me one day with this bugger of a problem in the plugin: it'd hang and hang and not return anything. The problem turned out to be trying to call debug() on a component instance, and it was taking forever to serialize the data across the wire from CF to Eclipse (or something like that). My response was "if it hurts to do that, quit doing that". Bob's persistent, though, and was having none of my attitude. From our conversation, the need for method timeouts was born. There's now a global preference for timeouts. Now that this feature is in there, I like it a lot. I've set my preference down real low... to 5 seconds. This keeps batches of tests flying and lets me know what tests are taking too damn long. When I want to actually run those long tests, I change the preference (quick and easy from within the View itself.. no window/preferences hooey). Admittedly, this timeout behavior is an initial stab. It's a big old sledgehammer approach (single setting controlling everything). In the future, this timeout behavior will likely be controllable at a more granular level. This was put on the radar a while ago when Bill and I started talking about what's coming up in MXUnit 2.0. Run multiple directories or components in the Navigator You should know by now that you can right click on any directory or Test component in the navigator view, right click, and select "Run MXUnit Tests". However, until now this was limited to a single directory (and its children, obviously) or component. And you couldn't select projects. This was modeled after the JUnit way of doing things. Turns out, the addition of the folder-level component path property was the key to enabling this "multiple select" feature. I won't get into the details, but the bottom line is that you can now select multiple directories, tests, and even projects. No more View "Chatter" When running a large number of tests, the top panel of the view scrolls to the bottom as the tests are run. In addition, all of the tests are selected so that the bottom panel will have any stack trace details immediately available. This visual cue that your tests are running is a holdover from when the plugin was first written, prior to using a progress bar. This was fine in normal day-to-day unit testing, but when running a lot of tests the constant updating of both panels created "chatter". On a fast machine, it was downright epileptic. With this update, the top panel no longer scrolls, and the bottom panel isn't updated until all tests are run. This creates a much quieter test run. If you mainly use the plugin for TDD, small-batch runs, etc, then the difference will not be very noticeable. If you run a lot of larger batches, you'll see the difference right away. Small stylesheet enhancements This is a small change, mostly for the bored and picky. When viewing the output of tests in the browser view, the styling was extremely basic. I'm no pixel pusher. So I made the styles customizable, with a bit of work. This will get easier down the road, but for now, simply navigate to your eclipse plugins directory, find the org.mxunit.eclipseplugin.... directory, and then drill down into the "style" directory. You'll find a stylesheet. Do as you please. Just don't delete the file. As I said, this is a silly little change for folk more visually inclined than I. So how do you learn more about all this stuff? You might not know it, but the plugin comes with integrated help. It's the QuestionMark icon to the right of the View. Click that thing, and then from there you can go to "Configure global preferences" to learn more about method timeouts and "Configure project properties" to learn more about the improvements to setting the CFC path property at any resource level, including the "empty path" indicator. The plugin's been working fine for me. Will this break things? It shouldn't... though I haven't tested on Mac or Linux. If the plugin behaves differently -- in bad ways -- email me on the MXUnit mailing list and I'll do my best to get things fixed up toot sweet. 'Cause toot sweet is how we roll. A Plea, from me to you, About Coding Faster This is completely unrelated to the plugin but is directly related to "working smarter, not harder". The MXUnit framework download comes with a directory named "cfeclipse". Inside are two directories: snippets and dictionary. Please, read the readme.txt files in there and spend 5 minutes installing the dictionary and snippets. Use the copysnippets.xml ant task to copy the snippets and the keyboard shortcuts to your snippets directory. And start using the snippets! You can crank out lots of boilerplate code with the snippets and focus your energies on writing just the code you need to write. Done. Test and Be Happy!

Special Thanks to FlexBuilder Team

Tuesday, November 25, 2008

This year at MAX, I had the privilege of speaking with several members of the FlexBuilder team during the "Flex Meet the Team" BOF. I went to that BOF with one question to ask, something along the lines of: "When you're programming Eclipse, how do you learn what APIs to use to achieve a given task". I know, that's a very broad question. Hear me out. The Eclipse framework is massive. Not Grand Canyon massive. Jupiter Massive. It's not just the programming APIs but the entire command/action framework which comprises gazillions of potential lines of XML configuration. It's actually not terribly complicated once you actually know what you're doing, but until you know what you're doing, it can be fairly intimidating. This is made both better and worse by the fact that there is an incredible amount of documentation, at least in API form, available. Better in that, hey, at least it's out there. Worse in that, hey, reading API docs doesn't really show you how to do something. Also potentially worse in that a lot of the examples online are older and so one can't be sure if those examples are relevant or deprecated. And Reading the schema for plugin xml extension points, while eye-opening, does not click right away.
So when I was asking my question, I wasn't asking "how do you do X", but "if you need to do X, how do you find -- in all this vast information available -- the example or the guide that you need to get you on the right path?"
To illustrate, let me give you a recent example. I'm tired of using this example, but it's relevant, so suck it up. I was refactoring the code inside the MXUnit plugin that collects tests and converts them to their CFC path. Imagine you've got a big ol' directory of tests, with subdirectories and whatnot. And you want to convert c:\inetpub\wwwroot\blah\tests\.... into blah.tests.XXXX for every test. You're probably thinking, "Simple. Just do a recursive directory list and for each file found, perform some string manipulation".
And that's how I did it originally. But here's the thing: once you start working with Eclipse, you start to realize that there's usually the right way, the wrong way, and "The Eclipse Way". And I wanted to use "The Eclipse way". My gut told me that this type of recursion was so common that Eclipse surely had a way to go about it. So in your head, think to yourself, "I need to loop over a directory, and all its children, and do stuff". If you know a little about Eclipse, you're thinking "How do I traverse resources?" or something like that.
So that's the problem statement: "How do you traverse resources and perform operations on each resource?"
Now... go look at the Eclipse API documentation and find the answer. Go on. Try it. I'll be here waiting.**
If I were asked this question, I'd probably be somewhat annoyed, especially at 8 PM after a few beers. "Do your homework, douche". "Read the f'n manual". "Ask the newsgroups". "Get away from me".
But not the FB guys. T, A, and S* were incredibly gracious with my probably stupid-seeming question. They were a complete delight to speak with! And they didn't blow me off. They engaged, and for that I am extremely grateful.
Both T and A told me that they struggled -- and to some degree continue to struggle with -- these kinds of problems. I don't know why, but I felt somewhat comforted hearing that. These are people putting out some kickass stuff, and they don't have everything memorized, either. For what it's worth, A told me he looks at the Eclipse source code itself, which I've only recently started doing but which was a lesson worth hearing again.
S, who is the in-house Eclipse guru, suggested among other things that I check out EclipseCon. This is kind of a pickle for me: I love programming the MXUnit plugin. I want to contribute more to CFEclipse (especially to the views that will NOT be replicated in Bolt when it comes out in 09). But I don't think I can justify the 3-4k it would cost to attend the conference considering that my only Eclipse programming work is open source. It's not like there's an MXUnit foundation! My company doesn't give a sh*t about my Eclipse programming, so there's no benefit to paying for my attendance. I wouldn't pay if I were them.
I'm hoping that, down the road, EclipseCon does what Adobe is doing this year with MAX: putting the presentations online for all to see.
Anyway, that's a long post just to say, "Thanks guys! You rock".
*Names changed. I'm honestly not sure if you're allowed to talk about people's names working on products. Better safe than sorry. Guys, you know who you are.
**Regarding the resource traversal question, it's simply resource.accept() passing in an IResourceVisitor. I was able to cut out nearly 100 lines of old shitty code and replace it with nice tight simple shiny code once I finally waded through the API and found what I needed. Took 2-3 hours to find where to look (you go down a LOT of dead ends) and less than an hour to get it all working. Once you see the finished product, you're like "Duh. Of course that's how it should be".

Advanced Unit Testing Patterns at Adobe MAX 2008

Wednesday, November 19, 2008

This morning, Bill and I delivered a session at Adobe MAX on Designing components for testability. Considering that the CF9 ORM session was scheduled at the same time, I was pleasantly surprised that we had a decent turnout. Thanks to all who showed up! A zip file is available here. This contains the slides plus all the code we showed. I forgot to mention this during the presentation, but there is an "appendix" directory in the code. it contains some samples of other components designed for testability. Most importantly though, it contains a Challenge. In there you'll find a working example of a PDF thumbnail generator... generating thumbnail images of PDF pages using CFPDF. There's a test for the component in there as well. The thing is... the test sucks. It's brittle. It's slow. The challenge is to refactor both the code and the test to make it easier to test, using the concepts discussed during the presentation. So go download the code, create a new project, and play! In the code/challenge directory is a README file explaining what to do. When you think you have something, ping us on the MXUnit Google group and we can hash it out. I purposely did not include my solution to the problem in the zip file! I want to see what people come up with on their own. Update The session recording is now available here. Enjoy

Yeah, but does it run on [ENTER YOUR CF SERVER HERE] ?

Thursday, November 6, 2008

Configuration for Testing Adobe ColdFusion, Railo, and Open Blue Dragon using Apache Virtual Hosts and MXUnit and Ant It may be redundant to say, but testing becomes more challenging as the number of platforms you have to support grows. So, what if you want to move your CFML apps to another engine or you want your apps to work on all engines? How do you do it? Well, duh, test it!

I'm going to describe a couple of things: (1) How I set up my environment for testing, and (2) how I leverage MXUnit and Ant to perform concurrent tests on each platform.

There's a number of different approaches to this problem (VWMare or VirtualBox come to mind). This particular method is not so sophisticated but should be portable to various virtual images targeted to be run on different OSs.

The following should allow any developer on any platform with a couple of hours to be able to put together something that allows him or her to test their application on ColdFusion, Railo, and Open Blue Dragon. And it won't cost you anything but some bandwidth, and hopefully the headache was mine.

The napkin design ...

mxunit-qa440

For the app I want to test (MXUnit, natually), I want each CFML server to point to the same set of files on disk. I don't want to have different versions of the app in different web roots. Think a about that - I would have test in one server, then copy to another, test there, copy to the other, and round and round trying to keep track of changes... eek! Another thing I want is the URLs I use to test my apps be descriptive and simple: http://adobe, http://railo, http://obd. Lastly, I want the ability to run my tests on each platform concurrently.

These are the general steps: 1. Download and install server software 2. Decide on a web root location 3. Edit your hosts file 4. Connect your CFML Engines to Apache 5. Create Apache Virtual Hosts 6. Configure your CFML Engines 7. Run Smoke Test 8. Build and Run Ant based MXUnit tests

1. Download and install server software: 1. Apache 2.2 2. Adobe ColdFusion 8 (Multi-instance) 3. Railo Server /Resin 4. Open Blue Dragon - Jetty/Ready2Run

I won't bore your with talk about installing these products, as most are well documented.

At this point you have Apache running on localhost:80, a ColdFusion instance on localhost:8301, Railo on localhost:8600, and Open Blue Dragon on localhost:8080. Don't worry if your ports are not the same, just as long as they don't conflict with one another, you should be ok.

2. Decide on a web root location. I use D:/webapps on Windows or /home/billy/webapps on Ubuntu

3. Edit your hosts file On Windows this should be in C:\Windows\system32\drivers\etc\hosts, Unix /etc/hosts.

On Windows it may only have one entry: 127.0.0.1 localhost

Add the following:

127.0.0.1 railo 127.0.0.1 adobe 127.0.0.1 obd

This tells your machine to resolve the names to your localhost address, 127.0.0.1.

4. Connect your CFML Engines to Apache

4a. Adobe: Adobe ColdFusion has the webserver connector tool, which does the work for you. This should be located in {CF-Install}/bin/wsconfig.exe. Check the Adobe documentation for details.

This will make some entries in {Apache}/conf/http.conf See below.

4b. Railo - Open up the {Apache}/conf/http.conf file and add the following at the bottom.

#Load Railo Module LoadModule caucho_module "{Railo-Install}/win32/apache-2.2/mod_caucho.dll"

Where {Railo-Install} is the full path to your installation; e.g. LoadModule caucho_module "C:/railo-3.0.0.005-resin-3.1.2-with-jre-windows/win32/apache-2.2/mod_caucho.dll"

4c. Open Blue Dragon - OBD does not have a web server connector, but you will need to do some URL re-writing in the virtual host config - a whopping 2 lines of code! See next section #5.

The bottom of my httpd.conf looks like this:

##########################################################
#
# Load CFML Engine Plugins
#
##########################################################

# JRun Settings
LoadModule jrun_module "C:/JRun4/lib/wsconfig/1/mod_jrun22.so"
<IfModule mod_jrun22.c>
JRunConfig Verbose true
JRunConfig Apialloc true
JRunConfig Ignoresuffixmap false
JRunConfig Serverstore "C:/JRun4/lib/wsconfig/1/jrunserver.store"
JRunConfig Bootstrap 127.0.0.1:51000
#JRunConfig Errorurl url <optionally redirect to this URL on errors>
#JRunConfig ProxyRetryInterval 600 <number of seconds to wait before trying to reconnect to unreachable clustered server>
#JRunConfig ConnectTimeout 15 <number of seconds to wait on a socket connect to a jrun server>
#JRunConfig RecvTimeout 300 <number of seconds to wait on a socket receive to a jrun server>
#JRunConfig SendTimeout 15 <number of seconds to wait on a socket send to a jrun server>
AddHandler jrun-handler .jsp .jws .cfm .cfml .cfc .cfr .cfswf

</IfModule>

#Load Railo Module
LoadModule caucho_module "C:/railo-3.0.0.005-resin-3.1.2-with-jre-windows/win32/apache-2.2/mod_caucho.dll"

Note: Make sure the mod_rewrite and mod_vhost_alias module lines are uncommented (top of file). These are needed to create virtual hosts and to do the URL rewriting for Open Blue Dragon 5. Configure Apache Virtual Hosts Each virtual host entry represents a unique host url I want to invoke for each CFML server.
#Make any requests for http://adobe get processed by the Adobe CFML engine

<VirtualHost adobe:80>
ServerName adobe
ServerAlias adobe
DocumentRoot "D:/webapps/"

<Directory "D:/webapps/">
Order allow,deny
Allow from all
Options Indexes SymLinksIfOwnerMatch
DirectoryIndex index.html index.cfm
</Directory>
</VirtualHost>



#Make any requests for http://railo get processed by the Railo engine

<VirtualHost railo:80>
ServerName railo
ServerAlias railo
DocumentRoot "D:/webapps/"

<Directory "D:/webapps/">
Order allow,deny
Allow from all
Options Indexes SymLinksIfOwnerMatch
DirectoryIndex index.html index.cfm
</Directory>

ResinConfigServer railo 6800

</VirtualHost>



<VirtualHost obd:80>
ServerName obd
ServerAlias obd
DocumentRoot "D:/webapps"

RewriteEngine     On
RewriteRule       ^(.*)$       http://localhost:8080$1  [P]


<Directory  "D:/webapps/">
Order allow,deny
Allow from all
Options Indexes SymLinksIfOwnerMatch
DirectoryIndex index.html index.cfm
</Directory>
</VirtualHost>
######################################################
#            End httpd-vhosts.conf
######################################################

Notes:

Each virtual host correlates to a host name in my hosts file. So, when the web browser requests http://adobe, my machine forwards that to Apache on port 80. Apache is configured, by default, to use Adobe ColdFusion to process any CFML file. Next, if I request http://railo, again, my machine forwards the request to Apache, but, this time Apache has the instruction to process ResinConfigServer railo 6800. This tells caucho_module to process requests on the server named "railo" (localhost) on port 6800. This is the port Resin is listening on. All results are sent back to Apache and then web browser.

Also note that the Document Root for all these virtual hosts is the same, d:/webapps.

6. Configure CFML Server

Open Blue Dragon

1. Since ODB does not have web connector, we will use Apache's URL rewriting capabilities. In the above, any request made to http://obd will be processed by http://localhost:8080, which is where OpenBlueDragon and Jetty are running.

2. You will also need to edit the jetty.xml configuration file to specify the common webroot. This is located in C:\jetty-6.1.12-OpenBD\etc\jetty.xml. In this file we have to tell Jetty about our d:/webapps. Find the <Set name="handler"> elements and add the following above the <New id="cfmlWebContext_1" class="org.mortbay.jetty.webapp.WebAppContext">...</New> element. Note that the cfmlWebContext_1 element points to a sample Open Blue Dragon app with a couple of CFML pages. You should be able to keep that there.

<Item>
<New id="mxunit_qa" class="org.mortbay.jetty.webapp.WebAppContext">
<Set name="contextPath">/</Set>
<Set name="war">D:/webapps/</Set>
<Set name="defaultsDescriptor">
<SystemProperty name="jetty.home" default="."/>/etc/webdefault.xml</Set>
<Set name="VirtualHosts">
<Array type="java.lang.String">
<Item>127.0.0.1</Item>
<Item>localhost</Item>
</Array>
</Set>
</New>
</Item>

Railo

In the Railo web administrator (http://localhost:8600/railo-context/admin/web.cfm), under Archives & Resources, create a Mapping. For the Virtual value enter a "/" and for the Resource value, enter D:\webapps. The primary should be Resource and I'm not sure what trusted does, but I left that unchecked.

ColdFusion

Nothing special is apparently needed. If you run into an issue you could create a mapping in the ColdFusion Administrator for "/" pointing to D:\webapps. But this was not needed for me.

7. Run A Simple Smoke Test

Now, you should be able to start all these servers and they should all be running. When developing I always start my servers from the command line. That way, I can watch for debug output to stdout instead of digging through log files.

C:\> C:\apache\Apache2.2\bin\httpd.exe C:\> C:\Jrun4\bin\jrun start dev C:\> C:\railo-3.0.0.005-resin-3.1.2-with-jre-windows\httpd.exe C:\jetty-6.1.12-OpenBD\> java -jar start.jar

A Quick Visual Inspection: Here's a basic web page with an iframe for each virtual host. Formatting HTML omitted. <iframe src="http://adobe/mxunit"></iframe> <iframe src="http://railo/mxunit"></iframe> <iframe src="http://obd/mxunit"></iframe>

qa-matrix

Naturally, I'm using MXUnit, but this would apply to any app you want to test. Again, the goal is to have a common webroot for all CFML servers.

8. Build and Run MXUnit Ant-based tests

Now here's where the real power comes in! I totally heart Ant's <parallel> task and would buy it a lobster dinner if I could.

At this point you can run your tests anyway you please - open a web browser, er eh, and click each page one at a time and visually compare results. But what if you could do this really really fast? How about running 100 or more tests on each server at the same time? And be able to repeat it? Enter Ant and MXUnit ... Details on the MXUnit Ant task can be found here and the Test Automation section here.

Luis Majano's ColdBox Framework has been reporting some issues with MXUnit when run on Railo. In order to help find these pesky critters, I set up an Ant build to run Luis's tests on Railo and Adobe at the same time.

<project name="MXUnitTask" basedir="." default="runtests">

<property name="mxunit.jar" value="ant/lib/mxunit-ant.jar" />
<taskdef name="mxunittask" classname="org.mxunit.ant.MXUnitAntTask" classpath="${mxunit.jar}"   />

<property name="test.dir" value="D:\webapps\coldbox\testing\tests\cases\beans" />
<property name="component.path" value="coldbox.testing.tests.cases.beans" />

<property name="output.dir" value="tmp" />

<target name="runtests" description="Run same tests on all servers concurrently">

<taskdef name="mxunittask" classname="org.mxunit.ant.MXUnitAntTask" classpath="${mxunit.jar}"   />

<parallel>

<!-- run Adobe Tests -->
<mxunittask server="adobe" port="80"
outputdir="${output.dir}/adobe"
defaultrunner="/mxunit/runner/HttpAntRunner.cfc"
verbose="true"
haltonfailure="false"
haltonerror="false">

<directory runner="/mxunit/runner/HttpAntRunner.cfc"
     remoteMethod="run"
     path="${test.dir}"
     packageName="adobe.${component.path}"
     componentPath="${component.path}"
     recurse="true"/>

</mxunittask>

<!-- run Railo Tests -->
<mxunittask server="railo" port="80"
outputdir="${output.dir}/railo"
defaultrunner="/mxunit/runner/HttpAntRunner.cfc"
verbose="true"
haltonfailure="false"
haltonerror="false">

<directory runner="/mxunit/runner/HttpAntRunner.cfc"
 remoteMethod="run"
 path="${test.dir}"
 packageName="railo.${component.path}"
 componentPath="${component.path}"
 recurse="true"
/>
</mxunittask>
</parallel>

</target>

</project>

Take note of parallel, which spawns a separate thread for each sub-task. This effectively cuts this test's time in half!

Tip: It's also possible to speed up any testing effort simply by leveraging the the parallel task but point the mxunit ant task to different directories.

Using the junitreport task (not displayed here), I can then print a report. I particularly like this one - 168 tests, 0 Failures, 0 Errors. No critters found here, but they may be lurking somewhere.

Happy testing,

bill


References:

Setting up Railo and Apache on Unix

Setting Up Railo and Apache on Windows

Open Blue Dragon

MXUnit

ColdFusion

Apache Ant

One Said Getter

Thursday, October 16, 2008

Why Am I Beating this Dead Horse?

My friend Adam, as he is wont to do, ranted. He wrote a screed on getters and setters, in his typical Adam rant style. Folk commented. This started a small discussion in the Twitter-verse on the topic, which in turn resulted in Ben's post. More comments. A post from Brian. More comments. More posts.

So I figured, what better way to drive traffic to our unit testing blog than to add more to the topic du jour? Whorish, I know.

Without further adieu, an MXUnit hombre's take on the topic. This is not a response to any of the authors or commenters in the posts mentioned above, although it does cover a lot of the same ground. This is an article about principles that appear to collide. At its core, this article is about Negative Capability.

Should you expose data? If so, how?

This is a two-part discussion. First, should you expose data? Or is the need to expose data an indication of poor design? Second, if you're exposing data, how do you do it? Methods or straight-up variable access via "this" scope?

Why exposing data is bad

This is important. It's the crux of this whole discussion. I believe this is the fundamental source of confusion/angst/rants about this topic.

The argument against exposing data is that when you have simple "Bean" objects that are merely data containers, you end up with an anemic domain. In your anemic domain, your simple objects don't do anything, but are passed around to other objects that do the work. Those objects then must fetch data from the bean, via getXXX(), or via this.XXX. This results in the question, "Why are the other objects asking the object for data and doing the work when the object should be doing the work itself?".

Herein lies the problem, the source of the confusion: Does the presence of getters/setters indicate an anemic domain, of an object not shouldering its responsibilities? Does it mean my object sucks?

The answer is, No. The answer also is, "it depends". Ben hates that answer.

I believe the first step in determining whether the data are accessible for the right reason lies in asking yourself a few questions: Am I exposing the data because other objects need that data to do work? Is the work in question rightfully theirs, or should I (the object) be doing this work myself? Am I exposing the data because an html page needs it? etc.

Good Encapsulation

Let's look at an example where an object with the data does its own work: the java String class. (Ben is smiling right now. Ben knows why.)

String has all kinds of nifty functions: replace(), indexOf(), replaceFirst(), subString(), etc. You use them thusly:

String myString = "I love ColdFusion";
int whereIsCF = myString.indexOf("ColdFusion");

String has the data, so it's natural that it should perform the operations on myString. It should do the finding of substrings. It should return new strings with replace(). It has the data, why should it require you to use another object?

Bad Encapsulation

Now, imagine if you had to work with Strings like this:

String mystring = "I love ColdFusion";
StringService service = new StringService();
int whereIsCF = service.indexOf(mystring,"ColdFusion");

That should look really weird to you. You should be thinking, "Yeah, that's dumb. String has the data, why pass the object to another object, which is going to in turn call string.getStringData() to get the bits and then do the finding, and then return the position? Don't make no sense."

In Sean Corfield's response to Ben's post, he described the pattern something like this:

myBean = createObject("component","MyBean");
myBean.setXXX("XXX");
myOtherObject.doSomethingWithBean(myBean);

If you think about it, that pattern is what's going on in our Bad Encapsulation example.

So when do I expose, and when don't I?

This will probably be the most subjective part of this post. So be it.

I believe if your object is going to be presented in some GUI (web form, eg.), then you have to expose your data. Use snippets. Use codegen. Whatever. Make it available. The alternative is to use the builder pattern. Sorry, Swing FanBoys, but this is the web we're talking about. I've tried using Builder to build webforms. It is untenable. 1) It virtually precludes the designer/developer workflow. 2) In all but the simplest webforms, your object will not have all the data it needs to properly build the form. It's going to need options for dropdown fields that are only tangentially related. It's going to need groups of radio buttons and checkboxes that may or may not relate at all to your specific object. Bottom line, for me: it's a pain in the ass to build webapps this way.

However, I also believe that as you're working with your object, and you find yourself falling into the pattern described above -- where you're merely filling up a bean and passing it around so other objects can do work -- then you're likely getting into iffy design territory. It should start to smell. You should be asking yourself, "Am I creating a StringService?"

Ok, I think I'm getting it, but....

But.... what about The Single Responsibility Principle? If my object does all the work, won't it turn into this monstrous God Object? Am I not simply reverting back to procedural code within my objects, thus creating false OO BS?

That, Detective Spooner, is the right question

The question here is, "How do you know which objects should be doing which work?". A common example is the Bean/DAO combination pattern.

  1. Should the "Bean" be a data container, and you pass the bean to the DAO, which calls all its getters in the SQL?
  2. Should the bean be responsible for its own persistence?
  3. Should the bean be responsible for its own persistence, but instead of the queries directly in the bean, it delegates them to a composed DAO?
  4. Should the DAO take a bean as an arg, or should it take the bean's raw data as separate args?
Buddy, if you came here looking for an answer to that question, you're S.O.L. What you'll get from me is more jibba-jabba. (However, for two excellent discussions, I highly recommend "Head First OO Analysis & Design" and Uncle Bob's "Agile Software Development, Principles, Patterns, and Practices". Look for "Single Responsibility Principle" in particular. Those authors get paid to give answers. I get not paid to not give answers.)

Let's assume for a minute you're not using an ORM or framework that provides persistence mechanisms (like Transfer, Reactor, DataFaucet, etc). What are the arguments for the options above? (The astute reader will note that at least one person will comment on this post or another one of the posts mentioned above that there are at least 5 more ways of approaching this, and that way XYZ is best, and then that's how the annoying comment-arguments get started. The astute reader will discover that those arguments miss the point entirely. The point is to think about why you're doing what you're doing. The point is that it's about the journey, not the destination.). Now, where was I? Oh, yes, the options above. Let's look at them briefly:

Bean + DAO: What if I need to swap out DAOs later? What if I change databases? What if I need to support multiple databases in my app? Isn't it a good separation of concerns to have the object not be concerned with its persistence? Isn't the nature of object persistence in general simply a sideshow act, i.e. objects live to do other stuff, and the fact of their persistence is tangential? Isn't God really a woman? But if my database changes, now I have to make a change in two places (Bean and DAO), not one. Doesn't that violate orthogonality? Doesn't this result in high coupling whereby the DAO must know about the internal workings of the bean, to some degree?

Bean does it all itself: Am I really going to need to change databases? Really? Isn't that violating YAGNI? Do I really need to support multiple databases? If the bean does it all itself, doesn't that support the principle that I should only ever need to change something in one place (i.e. similar logic not strewn about the system in various places)? But doesn't this violate the Single Responsibility Principle, i.e. that a class should only ever have one reason to change? But, honestly, isn't the whole reason for this class's existence to be tossed about from form to database and back again? Is it really violating SRP if its responsibility IS to act as a mediator between user and database? Is this kind of schizophrenic back-and-forth normal?

Bean delegates to DAO: But what do I gain? The DAO is still calling the getters. If the DB changes, I still have to change code in two places. However, the "calling" code doesn't know that. it just merrily calls bean.save(form). So... no changes in client code. Also, makes it easy for mocking the DAO in unit tests. But with MXUnit's injectMethod, is that even relevant anymore since method mocking is so easy?

DAO takes bean's data as separate args: The DAO now needs to know nothing about the internal state of the bean. But... isn't the point of objects to encapsulate stuff so I don't need to be throwing around 12 different arguments into some object? And who wants functions that look like dao.save(bean.getThing(),bean.getThing2())...?

As I said... jibba-jabba.

Analysis is important. This back-and-forth is why you're doing what you do, reading this article, instead of saying "Welcome to Wal-Mart... is that a return?".

Part II: How to expose the data

We already talked about why you might, and might not, want to expose the data. In other words, when exposing the data is essential and when it's a design smell. Now, let's talk about how to expose the data. In CF, we have three choices: concrete getters/setters, generic getters/setters, and the "this" scope.

The arguments for getters and setters

  • They abstract the underlying data model (what if I want to keep my data in a struct named variables.instance and not in 'this' scope? what if my model changes?)
  • Insulate client code from under-the-hood changes
  • Enable lazy data loading
  • No additional typing overhead... just use snippets
  • Via API documentation, they very clearly show exactly what data are publicly available
  • The provide the ability to perform custom behaviors when setting or getting (this is the biggun')

The arguments for generic getters/setters (get('key'))

  • They abstract the underlying data model without requiring so much typing/extra code
  • They appeal to the dynamic nature of ColdFusion. Specifying typed returns on concrete getters/setters really isn't a gain in a dynamic language, so who cares if generic getters/setters deal in Strings?
  • When the properties of the object change (new fields added, for example), no additional code is required because the generic getters/setters will already have taken care of it. This leads to more future-friendly objects.

The arguments for 'this'

  • YAGNI: You ain't gonna need the custom behaviors that concrete getters/setters allow for... 9X% of the time, you won't need any form of manipulation on the data... you're just getting it directly from the data struct. So 'this' works just fine most of the time
  • Unnecessary typing and overhead. Leads to more lines of code to maintain (when the DB changes, for example).

The answer from on high

3 Programmers walk into a bar. One says "this". One says "getter". One says "get". Getter pounds This's fists and says, "I Will Break You.". They get drunk and fight. The next morning they wake up, hungover, black-eyed. Get says, "I shoulda been a Wal-Mart shelf stocker". This says, "Yeah, but then you gotta clean up the crap from the back of the stalls when filthy customers hit the john and..." Getter says, "WTF dude?". You see, This had worked at Wal-Mart. He knew he'd rather deal with mundane, overworked questions of data access techniques any day over cleaning up after explosive asses in a Wal-Mart bathroom.

CTRL-Space-Learn

We CF programmers are underserved in the IDE space. Our tooling, frankly, blows. I believe this will be remedied soon. And when it is, a new argument for getter/setter methods will emerge: Our tooling will make it worthwhile to do so. Let me explain.

When programming Java in Eclipse, NetBeans, IntelliJ, etc, you get code hinting. Code hinting has been around for, oh, I don't know, 25 years or so (SmallTalk IDE?). You're thinking, "yeah, but in CFEclipse, or CFStudio, or Dreamweaver, I get code hinting for CF Functions and tags". Which is true. But you don't get hinting for your own objects. This. flat. out. sucks. Sure, you can write your own dictionaries for CFEclipse, but that's just lipstick on the pig and is virtually unmaintainable. If you've never programmed in another language with proper code hinting, you don't know what you're missing. Let me tell you what you're missing.

You're missing one of the most powerful features of the IDE: the ability to teach yourself as you type. You're missing CTRL-Space-Learn.

When you're in the IDE, working on code that uses components unfamiliar to you (or even components you wrote yourself a while back), you have two choices for learning what functionality is available to you: You open up the component, or you view that component in the browser and get the CF component explorer output. It's a pain. In other languages, you simply type the name of your object instance, hit CTRL-Space, and up pops the methods available to you. Think of the power in that. C'mon, Think. Think of the efficiency. You're working in code that uses some BlogReader.cfc instance, and you want to write some stuff that spits out a list of all entries for a particular blog. You didn't write the code for BlogReader.cfc... someone else did. What do you do? Personally, I'd rather do this: reader = createObject(....); reader - period - CTRL-Space.... up pops everything I can do with that object.

If you're a programmer who likes to discover what objects do, the IDE is your best friend. I find this method of exploration to be one of the invigorating parts of programming with other people's code. When I'm working on the MXUnit Eclipse plugin, I'm CTRL-Space-Learn-ing about the Eclipse API every 30 seconds.

If a better IDE is released for CF, I do hope it includes code hinting for custom components without the need for a dictionary file. If it does, you'll see why getters/setters become much more attractive over generic getters/setters. I would assume, though, that this advantage would also be conferred to the 'this' scope, so this is not about 'this' vs. getter but about generic vs. specific.

So what are you saying, man?

Personally, having tried it all kinds of ways, I've settled into the habit of using private generic getters, in a base object, that encapsulate the underlying data structure. In my objects, which extend Base, I provide concrete getters/setters. They, in turn, set and fetch raw data via get('key') and set('key',value) functions, and possibly perform other operations on the data. This way, in a single place (get() in Base.cfc) I can do lazy loading. I only have a single place where the underlying data structure is defined (Base.cfc). And I can change functionality in Base.cfc to my heart's content without affecting any code that extends it. In addition, since I'm convinced we're going to get an IDE soon that supports hinting, I'm paving the way with concrete getters/setters for in-IDE API docs. I'll get CTRL-Space-Learn for CF. More importantly, my coworkers will get it for the objects I write, and I'll get it for the objects they write.

Concrete getters and setters aren't about you. They're about other people.

Obligatory Irrelevant Java Example

See StringService above

This Is Where Your Eyes Glaze Over (or, The Real Example)

Let's take a small sample application and see what it looks like through some of the various permutations described above. In fact, let's start off with something we didn't talk about at all: a completly procedural example that avoids all these silly pesky OO questions entirely

Straight up procedural


<!--- act_ShowFeeds.cfm --->
<cfquery name="Feeds" datasource="#application.dsn#">
select FeedID, FeedURL, FeedType
from Feeds
order by FeedSortOrder Desc
</cfquery>

<cfoutput query="Feeds">    
    <cffeed source="#FeedURL#" query="feedentries">    
        <dl>
            <cfloop query="feedentries">
                <dt>
                <cfif Feeds.FeedType eq "Atom">
                    <a href="#listFirst(linkhref)#">#feedentries.title#</a>
                <cfelse>
                    <a href="#rsslink#">#feedentries.title#</a>
                </cfif>
                </dt>
                
                <dd>
                    <cfif len(feedentries.Summary)>
                        #feedentries.Summary#
                    <cfelse>
                        #left(content,200)#                    
                    </cfif>
                </dd>                
            </cfloop>
        </dl>            
</cfoutput>

Pretty straight forward: get a list of feeds from the DB, loop over them, and for each feed, print out its title (with link) and a snippet of its description. Different feed types require different behavior. This example doesn't contain any persistence of feed items (we'll add that below). But if it did, it'd most likely just be in other "action" files.

Single Object

<!--- dsp_ShowFeeds.cfm --->
<cfset reader = createObject("component","FeedReader")>
<cfset reader.renderFeeds()>

<!--- FeedReader.cfc --->
<cfcomponent name="FeedReader.cfc">
  <cffunction name="renderFeeds" output="true" access="public">
    <cfset var feeds = getFeeds()>
    <cfoutput>#printFeeds(feeds)#</cfoutput>
  </cffunction>
  
  <cffunction name="getFeeds" access="private">
    <cfset var getFeeds = "">
    <cfquery name="getFeeds" datasource="#application.dsn#">
    select FeedID, FeedURL, FeedType
    from Feeds
    order by FeedSortOrder Desc
    </cfquery>
    <cfreturn getFeeds>
  </cffunction>
  
  <cffunction name="printFeeds" access="private">
    <cfargument name="feeds" type="query">
    
    <cfoutput query="Feeds">  
      <cffeed source="#FeedURL#" query="feedentries">  
        <dl>
          <cfloop query="feedentries">
            <dt>
            <cfif Feeds.FeedType eq "Atom">
              <a href="#listFirst(linkhref)#">#feedentries.title#</a>
            <cfelse>
              <a href="#rsslink#">#feedentries.title#</a>
            </cfif>
            </dt>
            
            <dd>
              <cfif len(feedentries.Summary)>
                #feedentries.Summary#
              <cfelse>
                #left(content,200)#          
              </cfif>
            </dd>        
          </cfloop>
        </dl>      
    </cfoutput>
  </cffunction>  
</cfcomponent>

Have we gained anything here? Well, it's unit-testable to some degree. We can use it in multiple places easily (although we could do the same with cfinclude in our procedural example, too). Otherwise... pretty ugly. Biggest advantage is probably testability (however minimal).

Multiple objects, doing their own work

This is my ultra extended example. I'm including Base.cfc in here to provide context, although don't count that against the "cost" of the feedreader stuff, since the cost of the base object will be spread out over all objects that use it.

<!--- dsp_ShowFeeds.cfm --->
<cfset reader = createObject("component","FeedReader")>
<cfset feeditems = reader.getFeedItems()>

<cfoutput from="1" to="#ArrayLen(feeditems)#" index="item">
  <dl>
    <dt><a href="#item.getLink()#">#item.getTitle()#</a></dt>
    <dd>#item.getAbstract()#</dd>
  </dl>
</cfoutput>

<!--- Base.cfc --->
<cfcomponent name="Base.cfc">
  
  <cfset variables.instance = StructNew()>

  <cffunction name="init">
    <cfargument name="ArgumentsScope" type="struct" required="false" default="#StructNew()#">
    
    <cfset var key = "">
    <cfset var tmp = {}>
    <cfloop collection="#ArgumentsScope#" item="key">
      <cfif isDefined("set#key#")>        
        <cfset tmp[key] = ArgumentsScope[key]>
        <cfinvoke component="#this#" method="set#key#" argumentcollection="#tmp#">
        <cfset structClear(tmp)>
      <cfelse>
        <cfset variables.instance[key] = ArgumentsScope[key]>
      </cfif>
    </cfloop>
    
    <cfreturn this>
  </cffunction>

  
  <cffunction name="getDSN">
    <cfreturn "FeedReader">
  </cffunction>
  
  <!--- internal generic getters/setters --->
  <cffunction name="getValue" hint="returns the specified value from the Order Object" output="no" returntype="any" access="package">
    <cfargument name="name" hint="The variable you wish to have returned from the object" required="Yes">
    
    <cfif StructKeyExists(variables.instance,"#name#")>
      <cfreturn variables.instance["#name#"]>      
    <cfelse>
      <cfreturn "Undefined">
      <!--- could also pre-initialize the value here and return it, thereby providing lazy loading --->
    </cfif>
  </cffunction>
  
  <!--- note use of return this. I like me some method chaining --->
  <cffunction name="setValue" hint="sets a value in the variables.instance structure" output="No" access="package">
    <cfargument name="name" hint="The name of the value to set" required="Yes">
    <cfargument name="value" hint="The value to set" required="Yes">
    <cfset "variables.instance.#name#" = value>
    <cfreturn this>
  </cffunction>
  
  <cffunction name="queryToObjects" output="false" access="public" returntype="any" hint="converts a query into an array of objects. if query is empty, an empty array is returned">
    <cfargument name="query" type="Query" required="true"/>
    <cfargument name="cfcPath" type="string" required="true"/>

    <cfset var objects = ArrayNew(1)>
    <cfset var obj = "">
    
    <cfloop query="arguments.query">
      <cfset obj = QueryToObject(query,arguments.query.currentrow,arguments.cfcPath)>
      <cfset arrayAppend(objects,obj)>
    </cfloop>    
    
    <cfreturn objects>
  </cffunction>

  <cffunction name="queryToObject" output="false" access="public" returntype="any" hint="converts a single row of a query into an object">
    <cfargument name="query" type="query" required="true"/>
    <cfargument name="row" type="numeric" required="false" default="1"/>
    <cfargument name="cfcPath" type="string" required="false" default="" hint="Must either pass the full path to a CFC OR a pre-created object"/>
    <cfargument name="object" type="any" required="false" default="" hint="A pre-instantiated object. Use in place of the CFCPath argument"/>
    
    <cfset var columns = ListToArray(query.columnlist)>
    <cfset var col = "">
    
    <cfif isSimpleValue(arguments.object)>
      <cfset arguments.object = createObject("component","#arguments.cfcPath#").init()>
    </cfif>
    <cfloop array="#columns#" index="col">
      <cfif StructKeyExists(arguments.object,"set#col#")>
        <cfinvoke component="#arguments.object#" method="set#col#">
          <cfinvokeargument name="#col#" value="#Query[col][arguments.row]#">
        </cfinvoke>
      <cfelse>
        <cfinvoke component="#arguments.object#" method="setValue" name="#col#" value="#Query[col][arguments.row]#">
      </cfif>
    </cfloop>
    
    <cfreturn arguments.object>
  </cffunction>
</cfcomponent>

<!--- FeedReader.cfc --->
<cfcomponent name="FeedReader.cfc" extends="Base">
  
  <cffunction name="getFeeds" access="public" hint="returns an array of feeds">
    
    <cfset var storedfeeds = getStoredFeeds()>
    <cfset var feeds = ArrayNew(1)>
    
    <cfloop query="storedfeeds">
      <cffeed source="#FeedURL#" query="feeditems">
      <cfset feeds.addAll( queryToObjects( feeditems, "com.blah.FeedItem") )>
    </cfloop>
    
    <cfreturn feeds>
  </cffunction>
    
  <cffunction name="getStoredFeeds" access="private" extends="Base">
    <cfset var getFeeds = "">
    <cfquery name="getFeeds" datasource="#getDSN()#">
    select FeedID, FeedURL, FeedType
    from Feeds
    order by FeedSortOrder Desc
    </cfquery>
    <cfreturn getFeeds>
  </cffunction>
  
</cfcomponent>

<!--- FeedItem.cfc --->
<cfcomponent name="FeedItem.cfc" extends="Base">
  
  <cffunction name="init">
    <cfargument name="FeedEntryID" type="numeric" required="false" default="0"/>
    <cfset super.init(arguments)>
  </cffunction>
  
  <!--- bunch of getters and setters, generated in about 30 seconds from snippets, removed for sake of space --->
  
  <!--- make these private b/c we don't want to give people access to them... they're dangerous and we'll abstract it for them down below --->
  <cffunction name="getRSSLink" access="private">
    <cfreturn getValue("RSSLink")>
  </cffunction>
  <cffunction name="getLinkHREF" access="private">
    <cfreturn getValue("LinkHREF")>
  </cffunction>
  
  <cffunction name="getLink" returntype="string" hint="abstracts out the bullshit between Atom and RSS">
    <cfif len(getlinkHREF())>
      <cfreturn listFirst(getlinkHREF(),",")>
    <cfelse>
      <cfreturn getRSSLink()>
    </cfif>
  </cffunction>
  
  <cffunction name="getAbstract" access="public">
    <cfif len(getSummary())>
      <cfreturn getSummary()>
    <cfelse>
      <cfreturn left(getSummary(),getAbstractLength())>
    </cfif>
  </cffunction>
  
  <cffunction name="markAsRead">
    <cfset var markRead = "">
     <cfquery datasource="#variables.instance.dsn#" name="markRead">
      UPDATE FeedEntries
      SET read_flag = 1
      WHERE FeedEntryID = #getFeedEntryID()#
     </cfquery>
     <cfreturn markRead>
  </cffunction>
    
</cfcomponent>

This looks like a lot, I know. Consider this, though: This object is now extremely testable. I can unit test FeedReader.cfc easily by overriding the getStoredFeeds function using injectMethod and a spoof query, and I can create a query of local RSS And ATOM feed files that would be used just for testing. I can test the abstract and link formatting in the FeedItem.cfc without touching any feeds at all. Hell, if I wanted, I could even abstract cffeed into a separate function, override it in a unit test with a query spoof, and test the functionality of FeedReader.cfc's getFeeds() method in a snap... no reading feed files... just testing that if given a query of feeds from different sources, I get a full array of feed objects back representing each item in each feed.

Extra points for the easy refactoring that can be done in Base.cfc. Hint: look for eerily duplicate code

Multiple objects; Bean spreading its data to whoever wants it

I'm not going to write this example. But consider it to be the Bean + DAO + Gateway.... example. In a nutshell, you'd have a FeedItem.cfc which had almost no behaviors. Maybe it wouldn't even have the link and abstract functions... Maybe it'd delegate that to a "FeedFormatter.cfc". I dunno. And then you'd be passing your bean to the DAO, and the DAO would be calling FeedItem.getTitle() and whatnot for persistning that to the local store of FeedItems. The markAsRead functionality, for example, would look something like this:

<cfcomponent name="FeedItemDAO.cfc">
<cffunction name="markAsRead">
  <cfargument name="FeedItemBean" type="any" required="true"/>
  <cfset var markRead = "">
   <cfquery datasource="#variables.instance.dsn#" name="markRead">
    UPDATE FeedEntries
    SET read_flag = 1
    WHERE FeedEntryID = #FeedItemBean.getFeedEntryID()#
   </cfquery>
  <cfreturn markRead>
</cffunction>
</cfcomponent>

Or, more likely, you'd probably have a single save() function, and your code might look something like this:

<cfcomponent name="FeedReader.cfc">
  
  <cffunction name="markFeedItemAsRead">
    <cfargument name="FeedItemBean" type="any" required="true"/>
    <cfset FeedItemDAO.read(FeedItemBean)>
    <cfset FeedItemBean.setRead(true)>
    <cfset FeedItemDAO.save(FeedItemBean)>
  </cffunction>
  
</cfcomponent>

<cfcomponent name="FeedItemDAO.cfc">  
  <cffunction name="save">
    <cfargument name="FeedItemBean" type="any" required="true"/>
    <cfif feedItemBean.getFeedItemID() EQ 0>
      <cfset insert(FeedItemBean)>
    <cfelse>
      <cfset update(FeedItemBean)>
    </cfif>
  </cffunction>
  
  <cffunction name="update" access="private">
    <cfargument name="FeedItemBean" type="any" required="true"/>
    <cfset var update = "">
    <cfquery datasource="#variables.instance.dsn#" name="update">
    update FeedItems
    set content = <cfqueryparam cfsqltype="cf_sql_varchar" value="#FeedItemBean.getContent()#">,
    whatever = #getWhatever()#,
    readdate = #getReadDate()#,
    readflag = #getRead()#
    where FeedItemID = #FeedItemBean.getFeedItemID()#  
    </cfquery>
    <cfreturn update>
  </cffunction>
</cfcomponent>

And I ask you, in this case: What do you gain by putting that functionality into the DAO and out of FeedItem itself? What do you lose? How does your answer change if, instead of this being an internal application, you're developing an open source feed reader app, written in CF, and you want it to work with SQL Server, Oracle, Derby, and MySQL?

I'm serious. Think about those questions. Don't blow them off. Reading crap like this on the interwebs doesn't mean squat if you don't wrestle with this stuff.

FutureShock

Brian Kotek wrote "It's a principle, not a blindfold" about YAGNI. I like that. I don't think we should consider how our feedreader app is going to need to grow 5 years from now. But we can at least make some reasonable assumptions about the future. Let's do a quick tally to see where we might stand, future-wise:

  • Looking at the ATOM format for linkHREF coming out of CFFEED... it's a comma-delimited list. Ugly. What if a link itself has a comma in it? I can see already that the logic for getting Link URLs is a source of potential bugs. getter +1, 'this' -1
  • Our display so far isn't very attractive. Quite 1996. We're going to want to punt it to design. Builder -1
  • Someone requested a link to the comments for a feed item, if one is available. How? Add getter to FeedItem.cfc that contains logic around the linkHREF and linkType fields. OO +1 vs. procedural
  • Someone requested that the author name appear. In ATOM feeds, this is easy. In RSS feeds, not so much. Gonna have to parse the name out of the AuthorEmail field. getter +1, 'this' -1
  • Some 12 year old kid with his gizmos and flashy stuff wants an iPhone page, whatever the hell that is. He wants it to work like iPhone pages, where you click the name of the blog post and the screen swooshes to the right and displays the full feed. WTF? OO +1 vs. procedural. Now, it's just a matter of writing HTML and javascript. No backend changes at all required. No duplication of feed reading/parsing/etc. required.
  • You knew it. You just knew it. Some dude wants PostgreSQL support. Everything almost works. But those damn "bit" fields... Separate DAOs +1
  • CF9 comes out and they add 3 more columns to the CFFEED query. You want to provide access to those fields in your FeedItem object, but you still want to retain support for CF8. getter +1? 'this' + 1? Oh, wait, it's an ugly field like the linkHREF field that requires you to *do stuff* to it. getter +1 (insert scream from some reader saying, "But that shit never happens!" here.)
  • You want to learn some Flex and think a new whizbang Flex front-end would be sweet for your feedreader. You want to pass your FeedItem objects across the wire rather than straight up queries. Will it work with your "smart" FeedItem object as it's currently written? Anemic domain + 1? You tell me.
  • Someone's blog URL has changed, and you update the database with the new URL. Next time your cffeed routine runs, though, it doesn't recognize the older feeditems as "read", and so your list of links now has a bunch of old junk that you did, in fact, already read. How hard is it to update your code to fix this?
  • Your app is slow. C'mon, n00b. So you decide to speed it up by making the feed reading multi-threaded. Big deal? T'aint nuthin? Do any of the designs above provide an advantage here?

Does context matter?

  • You've been reading about onMissingMethod in CF8 and you think, "Hey, that's a great way to provide zero-code implicit getters/setters. I think I'll do that!".
  • You hear that CF9 might include implicit getters/setters... does that eliminate all this malarchy entirely? What about the IDE -- CTRL-Space-Learn?
  • Do your answers to any of these questions change if you're working by yourself as a freelancer as opposed to working on teams with other people using your code?
  • Does clarity of API matter if you're working on a team of really smart people? Does the value of in-IDE API support diminish as the quality of team members increases? i.e. Does your answer to the specifc/generic/this conundrum differ based on the team in which the code will be used?

Summary

If you've gotten this far, you're in two camps: you didn't read the top and instead scrolled down here for the answers. Or you read this far and you're just hoping that the dude writing this says "OK, people, here's the deal".

Here's the deal

If your objects are anemic objects with no behavior at all, giving their data to other objects to do the work, ... that's a smell to me. This is why people say "getters are bad". More than likely, something has to be done with that data. Some behavior is likely required. If the object with the data isn't performing that behavior, then some other object must be doing it, and it's probably doing it by having the object in question passed into it, then calling its getters. You gotta ask yourself this question: Am I writing a StringService?. Should this object, my original object, be doing its own work?

If your objects have getters because, Dammit, you ain't writing no forms inside your objects, well, that makes sense to me. If you want objects to populate forms, you need to expose the data. It's that simple. People don't say "getters suck" for this reason. They're not talking about web forms. They're talking about "the-wrong-object-is-doing-the-job" problem. Stop your fretting, Peregrine Took.

If there's extremely little chance of swapping databases, providing multi-database support, etc, then why do you need separate DAOs? Fight amongst yourselves. I know where I stand on this one.

As for generic getters/setters, IDE support for custom object code hinting will change everything. Just you wait.

I could write another follow-up post simply on the testability aspect of the various approaches discussed above. I know, unit testing isn't done by the majority of CF Developers. But for those who do unit test, of the approaches above, which design decisions lead to easier-to-test code? Which provide the ability to "spoof" or abstract certain functionality for testing purposes in order to test other specific behavior?

Summary, for real this time

This post isn't about answers. It's about questions... about learning what questions to ask yourself as you make the design decisions you'll be faced with. Write yourself a small application and try it out in a few different ways: bean+dao. smart object doing the work. bean + composed DAO. What did you like? What felt right? If you had to change something big a half year from now, which of your 3 apps would you prefer to work on? Try using 'this' and generic getters/setters and concrete getters/setters. Assume a co-worker is going to be using your object in one of theirs. What would they rather work with? What gets them ramped up quicker? Try writing code that passes nothing but raw data into the DAO object such that the DAO doesn't call obj.getSomething().... did you like that? What does the code look like after you're finished with it? Imagine you have to add a few new columns.... ask 2 co-workers which code they'd rather maintain were they tasked with modifying your DAO/Bean stuff. And finally, try writing unit tests for the app, designed various ways. Were there differences in testability between the various apps?

These are the questions that matter. I said at the beginning of this post that it was about Negative Capability... the ability to hold contradictory, conflicting thoughts about a thing and be OK with it. In some ways, it means embracing the pain. I suppose in the context of this discussion, it means accepting that the answers to the questions "Should I expose data?" and "If so, how?" are influenced by factors not specifically related to the code itself. Code doesn't exist in a vacuum. People read it. People maintain it. IDEs parse it. Et cetera. I'm not suggesting that "everything is relative" and "it always depends" and "there are no right answers". There are! I do, though, believe that in order to answer these two questions, the answers to *the other questions* will help get you there. Take heart, Merry!

Of course, you could always trade in your keyboard for a Wal-Mart smock. "Cleanup, Women's Stall 4...."

Setting up MXUnit with CruiseControl


In-The-Trenches bad-ass, CF Bill, has been hard at work documenting how to get MXUnit integrated into CruiseControl (http://cruisecontrol.sourceforge.net/). This one goes on our list for sure! http://cf-bill.blogspot.com/2008/10/setting-up-mxunit-ant-and-cruisecontrol.html

OT: Podiobooks

Tuesday, October 14, 2008

I have a fairly long commute. For the longest time, I'd listen to NPR Morning Edition on the way in and Fresh Air on the way home. Over the past few years, Morning Edition has become doom-and-gloom all morning. I'd get to work so bummed out that it'd take an hour to get my mind right again. War this. Economy that. Death. Poverty. Woe. Despair. Blah. Over Christmas this past year I bought an iPhone, which was an unusual extravagance for me. Nonetheless, buyer's remorse in check, I put all my songs on it, and they replaced NPR on my commute. After a few months, though, I grew tired of listening to them. Meanwhile, I had been listening to a lot of podcasts (java posse, flex show, cfweekly, etc), but with an hour and a half in the car each day, I would burn through them pretty quickly. Then, at CFUnited this past year, I was talking to Brian Meloche and he recommended I check out the "Earth Core" series. I was like, uh, huh? What's Earth Core? I asked. "Oh, it's a book you can listen to on your iphone", Brian said. From me, the QuestionOfAllQuestions: "For free?" Yep. You might be thinking, "dude, it's 2008, get with it." But understand: I take books very seriously. While I've come to expect high quality from (free) open source software, I harbor no such illusions about literature. Basically, until I started listening to the podcasts mentioned below, I'd have blown off the concept. "if it's free, it sucks", and I'd have dismissed it. Still, trying to keep an open mind, I added a note to my iPhone : "Check out EarthCore". And then I forgot all about it. A few weeks later I stumbled into my notes and, lo and behold, saw the EarthCore thing. So I went into iTunes, searched for it, and it showed up. Sweet, I thought. I downloaded it, started listening to it, said "it's no Shakespeare but it's better than NPR and there are cool thingies that cut people to shreds", and had a lot of fun. Maybe this free book on the phone thing ain't so bad after all. After getting that under my belt, I decided to check out what else lurked on the interwebs for a dude like me just lookin' for a better drive. I happened onto the Max Quick series by Mark Jeffrey. Now this.... this... this was right up my alley. Space/time rifts. The DreamTime. Mirror travel. Alien/humans who populated the earth long ago. Fun characters. Good writing. Articulate. Philosophical. Excellent!!! As soon as I finished MaxQuick 1, I downloaded Part 2. What I didn't know at the time was that MQ2 wasn't finished being recorded yet. So after burning through all the recorded chapters, I found myself in Wait Mode. Wait Mode Sucks. Meanwhile, having been bitten by the podiobook-commute bug, I sought out some others. I listened to "Brave Men Run" by Matthew Wayne Selznick. That one took me a bit to get into, but once I did I was hooked. More SciFi goodness, but with an 80s twist. It was "Pretty In Pink" meets X-Men. Then, on to my current one, "7th Son" by J.C. Hutchins. Human Cloning, "Do I have a soul or not?" type stuff. Again, thoroughly intriguing and enjoyable. I'm fairly certain that when I finish Part I I'll move right onto the next one rather than look right away for a different author. Here's what I'm thinking so far:
  1. Most of the Podiobooks I've run into are SciFi. I could speculate on why this is, but I won't. Suffice it to say, I've not run into anything that hasn't been in some way related to genomics/cloning/aliens. Not that I'm looking for Anne Tyler or anything, mind you. I'm still trying to decide how much I'm willing to pay for Audiobooks of the classics. Is 30 bucks too steep for War and Peace? I don't know. I've tried twice already to make it through the hardback.... maybe being stuck in a car with it will put me over the finish line.
  2. Babble books suck. Big time. If you see them in iTunes and say "holy crap they have Charles Dickens for free on iTunes!", don't bother downloading them. It's like listening to an elevator talk to you. Except worse.
  3. Up until about a year ago, I was a fairly aggressive driver. I hit the hammer lane and that's where I stayed till I get home. Folk in my way were to be tailgated with malice until they merged (way too slowly) back into the minivan lane where they belong. However, a while back, I decided that I needed to become a right-lane kind of guy. Relax. Enjoy the ride. But damn... it's hard when you're a type A++ who just wants to get the hell home. And here's the great thing about the podiobooks: they've worked wonders in converting me into a much calmer right-lane citizen. It takes me a few minutes longer to get home, but the commute is far more mellow.
If you're a commuter with an iPod or iPhone, and you like listening to stories, give these podiobooks a shot. You can get them all within iTunes. And there are many, many more. If you're already a podiobook listener, please let me know what's worth checking out, too!