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

  • http://www.garyrgilbert.com/blog/index.cfm/2008/11/6/Installing-Railo-3-on-Linux-CentOS-Part-1
  • http://www.hockeypfef.net/index.cfm/2008/9/29/Railo-Server-and-Apache

Setting Up Railo and Apache on Windows

  • http://www.railo.ch/blog/index.cfm/2008/5/2/Railo-and-Apache

Open Blue Dragon

  • http://www.openbluedragon.org/

MXUnit

ColdFusion

  • http://www.adobe.com/products/coldfusion/

Apache Ant

  • http://ant.apache.org/
  • http://www.thecrumb.com/