Data-Centric Requirements Gathering

Software development is hard. Consider that in developing custom software, you’re creating a new and unique artifact for the client every time. Not only that, you’re creating it for someone who doesn’t really know what shape that artifact should take until after they see it. The costs and long time lines involved only complicate the process, and development budgeting and estimation is considered something of a dark art even by those who are relatively good at it. How then, can we expect to get the client involved with and, dare I say it, excited about the development process?

More after the break.

Read the rest of this entry »

Invoking Coldfusion UDFs from a JSP

In the interest of full disclosure, I can’t think of a very practical use for this facet of Coldfusion/J2EE interoperability. It’s nice to know that, should an emergency arise in which I need to execute a UDF in a JSP or face some horrible consequence, I will now be able to rise to the occasion; however, my employer might not share in my comfort, since this purely academic, theoretical work was done entirely on the company’s dime.

What I wanted to accomplish with this exercise was to create an anonymous inner class that would execute some logic defined in a Coldfusion page, and I wanted to do so without relying on any external libraries, Java or Coldfusion. To get us started, let’s take the example of listing out the files in a given directory, only showing files that match a certain extension (in this case, “cfc”). One way of doing this in Java is with the following code:

1
2
3
4
5
String[] fileNames = (new java.io.File("/home/me/some/directory")).list(new java.io.FilenameFilter() {
  public boolean accept(java.io.File file, String name) {
    return name.endsWith(".cfc");
  }
});

This code creates what’s called an “anonymous inner class”. One usually sees anonymous inner classes when doing GUI work (or at least that’s where I saw them back when I did some GUI stuff back in the late 90’s) with EventListener objects and such. These classes are simply informal extensions of existing classes where you explicitly fill out the methods declared as ‘abstract’ in the parent. The accept method of the java.io.FilenameFilter class is abstract, so in order to instantiate an object we must first fill out that method.

These anonymous inner classes cannot be created in Coldfusion (as far as I can tell). If you absolutely need an anonymous inner class, you have to do some JSP hacking, like so:

   Downloadsample.cfm
1
2
3
4
5
6
<!--- call the jsp that sets up the 'filefilter' object in the 'request' scope --->
<cfset GetPageContext().include("anonymous.jsp")/>
<!--- get a file object for the given directory --->
<cfset directory = createObject("java", "java.io.File").init("/home/me/some/directory")/>
<!--- create the fileList array containing the filtered file list --->
<cfset fileList = directory.list(request.filefilter)/>
   Downloadanonymous.jsp
1
2
3
4
5
6
<% // here we're just setting the 'filefilter' object to an instance of the anonymous inner class
request.setAttribute("filefilter", new java.io.FilenameFilter() {
  public boolean accept(java.io.File file, String name) {
    return name.endsWith(".cfc");
  }
}); %>

Note: as a general rule, if you’re passing things back and forth from Coldfusion pages to JSPs through the request object, make sure you always use variable names that are all lower case, otherwise one context or the other might not pick it up.

Okay, now the fun part: What if we want to use our own UDF for an anonymous inner class? Well, when you create a function in Coldfusion, the compiler creates a subclass of the coldfusion.runtime.UDFMethod class; the function is subsequently called through an invoke method. In order to execute the logic wrapped up in the cffunction tag, we’re going to have to use the Java Reflection API — but first, let’s look at the Coldfusion page that will be creating the UDF:

   Downloadudftest.cfm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!--- first we create our UDF for filtering filenames --->
<cffunction name="filterFunction" returntype="boolean">
  <!--- the method signature must match that of java.io.FilenameFilter.accept(java.io.File, java.lang.String) --->
  <cfargument name="fileObject" required="true"/>
  <cfargument name="filename" required="true"/>
 
  <!--- for this example we're listing all files that end with '.cfm' --->
  <cfreturn filename.endsWith(".cfm")/>
</cffunction>
 
<!--- we need to add a couple objects to the request scope so that the jsp can see them --->
<!--- remember to use lowercase names so the process doesn't hork --->
<cfset request.pageobject = getPageContext().getPage()/>
<cfset request.myfunction = filterFunction/>
 
<!--- now we process the jsp, which will put our function in an anonymous inner class --->
<cfset GetPageContext().include("udffilter.jsp")/>
 
<!--- list out the directory (using the current directory) using our new filterobject --->
<cfset directoryListing = createObject("java", "java.io.File").init(expandPath(".")).list(request.filterobject)/>
<cfloop index="k" from="1" to="#ArrayLen(directoryListing)#">
  <cfoutput>#directoryListing[k]#</cfoutput><br/>
</cfloop>

And the corresponding JSP, which is absolutely rife with reflection code:

   Downloadudffilter.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<%
try {
    // the following must be declared 'final' so they can be accessed in the anonymous inner class
    // first we pull the udf out of the request object, followed by the page object
    final Object o = request.getAttribute("myfunction");
    final Object page = request.getAttribute("pageobject");
 
    // the 'args' array will hold the arguments we need to invoke the udf's underlying UDFMethod.invoke method
    final Object[] args = new Object[4];
 
    // we use the java reflection api to pull out the method we want to call
    Class[] paramTypes = new Class[] { Object.class, String.class, Object.class, args.getClass() };
    final java.lang.reflect.Method method = o.getClass().getMethod("invoke", paramTypes);
 
    // here we set up the actual anonymous inner class
    java.io.FilenameFilter filter = new java.io.FilenameFilter() {
            // note the method signature is the same as the one declared in our .cfm
	    public boolean accept(java.io.File file, String name) {
		args[0] = o;
		args[1] = null;
		args[2] = page; 
		args[3] = new Object[] { file, name };
		boolean returnValue = false;
		try {
                    // invoke the actual udf. the returntype is 'bool', which is wrapped with a java.lang.Boolean
		    returnValue = ((Boolean) method.invoke(o, args)).booleanValue();
		} catch (Exception e) { }
		return returnValue;
	    }
	};
 
    // the filter has been created. assign it to the request scope and let's get out of here
    request.setAttribute("filterobject", filter);
} catch (Exception e) {
    // generic error 'handling'
    out.println(e.toString());
}
%>

It’s somewhat tricky to follow if you’ve never used the Java Reflection API, but there it is: a Coldfusion UDF invoked from a JSP. I can’t speak about its performance, its maintainability, or its potential applications, but it’s certainly ‘neat’. I only hope my employer concurs.

Using Java Properties files for multiple environments

Most of the time I have two environments: a local environment where I do the programming and testing, and a ‘live’ environment where the application is deployed. Sometimes, depending on the size of the project, I also have a testing environment and a staging environment. That’s a lot of places for individual environment customizations to clog up deployment. I can’t count how many times I’ve been bitten by forgetting to modify an Application.cfm file to point to the correct DSN on a given environment.

So to make things easier, or at least such that I never have to edit a file during deployment, I have a single .properties file per environment, usually something very simple:

   Download.properties
1
2
3
application.dsn=my_test_dsn
application.name=My Test Application
application.root=someRoot

For sites using PayFlowPro, I usually add an application.debug=true property as well, so that I can bypass the actual charge to get through specific use cases during testing.

The properties are loaded in Application.cfm as follows:

   Downloadapplication.cfm
1
2
3
4
5
6
7
8
9
10
11
12
13
<cfif not isDefined("application.properties") or isDefined("url.reload")>
  <cfset propConfig = CreateObject("component", "utils.PropertyCreator")/>
  <cfset properties = propConfig.getProperties(".properties")/>
</cfif>
 
<cfapplication
   name="#properties['application.name']#"
   sessionmanagement="yes"
   setclientcookies="yes"
   sessionTimeout="#CreateTimeSpan(0, 0, 30,0)#"
   />
 
<cfset application.properties = properties/>

The file that takes care of the actual file parsing is located in the utils directory:

   Downloadpropertycreator.cfc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<cfcomponent displayName="PropertyCreator">
 
  <cffunction name="configure" access="public" returntype="void">
    <!--- currently no initialization is required --->
  </cffunction>
 
  <cffunction name="getProperties" access="public" returntype="any"
	      hint="Creates a struct (like java.util.Properties) based on a Java .properties file">
    <cfargument name="fileName" type="string" required="true"
		description="The name of the .properties file, relative to your application root"/>
 
    <cfscript>
      properties = CreateObject("java", "java.util.Properties").init();
      propFile = CreateObject("java", "java.io.FileInputStream").
      init(javaCast("string", expandPath(fileName)));
      properties.load(propFile);
    </cfscript>
 
    <cfreturn properties />
  </cffunction>
</cfcomponent>

Simple. Now I deploy a .properties file one time on a per-environment basis, and I need not worry about modifying files during deployment, unless I’ve added a new property. Each environment uses the same Application.cfm.

And of course the standard security warning: if you’re going to put sensitive information in the .properties file, please make sure that a user can’t request it via HTTP. I usually have the file in its own directory with a separate Application.cfm that simply says <CFABORT/>

Coming soon: generating configuration files for 3rd-party libraries based on attributes in the .properties file.

Dynamically generating PowerPoint presentations in Coldfusion

Because we can.

Seriously, there are probably hundreds of good reasons to do something similar to this, which is why I did it (my reason: a paycheck). However, the underlying mechanic is by far more important that the specific implementation, and that mechanic is: Coldfusion Is Java, and there are free Java libraries for just about anything.

To accomplish this feat (for lack of a better word) I used Apache’s POI project, a pure-Java library for the creation and manipulation of some of Microsoft’s Office formats. Given that my hosting environments were such that I didn’t have access to the Coldfusion ‘lib’ directory, these POI libraries had to be loaded dynamically. For that I used Mark Mandel’s JavaLoader Coldfusion library.

Once JavaLoader was installed on the server, I uploaded both poi-3.0.2-FINAL.jar and poi-scratchpad-3.0.2-FINAL.jar (which is needed for the PowerPoint objects) to the application directory and used the following script to create a test PowerPoint slide:

View CodeCOLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<cfscript>
  // initialize the JavaLoader library and load up POI and its Scratchpad
  loadPaths = ArrayNew(1);
  loadPaths[1] = expandPath("poi-3.0.2-FINAL-20080204.jar");
  loadPaths[2] = expandPath("poi-scratchpad-3.0.2-FINAL-20080204.jar");
 
  loader = createObject("component", "javaloader.JavaLoader").init(loadPaths);
 
  // create the powerpoint presentation object
  ppt = loader.create("org.apache.poi.hslf.usermodel.SlideShow").init();
 
  // exactly like it says: create a slide, add a title, get the text run and
  // set its text to a heartwarming quote from a futuristic news anchor
  ppt.createSlide().addTitle().getTextRun().setText("Kittens give Morbo gas.");
 
  // modify the request output stream to dump to a binary file,
  // using the correct content-type for Microsoft PowerPoint
  context = getPageContext();
  context.setFlushOutput(false);
  response = context.getResponse().getResponse();
  out = response.getOutputStream();
  response.setContentType("application/vnd.ms-powerpoint");
  response.setHeader("Content-Disposition", 'inline; filename="HelloMorbo.ppt";');
  ppt.write(out);
  out.flush();
  out.close();
</cfscript>

A word of caution: Some of the files created by the POI PowerPoint library cannot be opened with Microsoft PowerPoint — but they can be opened with OpenOffice.org’s slideshow viewer. So if you, like me, are using OpenOffice.org to test your files, you will no longer need to be puzzled at why your client calls you up at 3:00 in the morning screaming vaguely about a broken PowerPoint generator.

I realize that if one is unfamiliar with either POI or JavaLoader, this solution seems like a whole bunch of information all at once to generate a pretty crappy presentation. However, with a little bit of education and a whole lot of patience you would be surprised what one programmer can do. For a paycheck.

Downloading Files Through a GZ or Zip Filter

It doesn’t come up often, but every now and then I have a need to make available a fairly large text file (or something similar) from my web server, and I think to myself, “self, wouldn’t it be fantastic if I could zip this file on the fly?” Well, it turns out that since ColdFusion is Java, zipping things on the fly is quite easy.

View CodeCOLDFUSION
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<cfscript>
  // the following code sets up the http response to send a file instead of a normal webpage
  context = getPageContext();
  context.setFlushOutput(false);
  response = context.getResponse().getResponse();
  out = response.getOutputStream();
  response.setContentType("application/x-gzip");
  response.setHeader("Content-Disposition", "inline; filename=" & '"' & filename & '";');
  response.setHeader("Content-Encoding", "gzip");
 
  // here we're creating the zipped output stream to which we'll write the file's contents
  gzip = createObject("java", "java.util.zip.GZIPOutputStream").init(out);
 
  // next we open the file on the webserver that we want to zip
  // -- don't forget to use 'expandPath()' --
  inStream = createObject("java", "java.io.FileInputStream").init(expandPath(filename));
 
  // a cheap way to create a temporary buffer
  // i didn't write this; i wish i could find where i got it so i could give proper credit
  byteArray = repeatString(" ", 1024).getBytes();
 
  // generic buffer-copy code
  len = inStream.read(byteArray);
  while (len gt 0) {
    gzip.write(byteArray, 0, len);
    len = inStream.read(byteArray);
  }
 
  // close all the open file and stream objects
  inStream.close();
  gzip.close();
  out.flush();
  out.close();
</cfscript>

I haven’t tried this with a Zip archive instead of GZip. I believe apart from simply switching ‘java.util.zip.GZIPOutputStream’ to ‘java.util.zip.ZipOutputStream’ you’ll have to fiddle with the ZipOutputStream’s setLevel(int) and setMethod(int) methods.

Please bear in mind that this script lends itself to insecure code. You must be extremely careful about how you arrive at the ‘filename’ variable: if you’re simply pulling a <cfset filename = url.filename/>, you’ll be in for a surprise the first time a user requests archive.cfm?filename=/etc/passwd.