Orange is my favorite color

Found something out yesterday that I thought was interesting and a little surprising. I have my main application, let’s call it “www”. I am creating a new codebase called “api” that will be a RESTful API for third-party consumers (more on that later). I want to have the two applications share the same Application scope so they can share my Coldspring bean factory and Transfer cache. The catch is that I don’t want session management for the API as it’s unnecessary.

The Objectives

  1. Share the Application scope
  2. Access the two codebases via different URLs and VirtualHosts
  3. Disable session management for the API

Pretty straightforward. Although it always starts out that way… :)

The Attempt

At first, I considered putting the bean factory in the Server scope. But my limited research seemed to indicate there were issues if you wound up with any application references. I am not sure if I have ever used the server scope before and putting my service layer in there didn’t seem like the best first attempt.

Instead I looked at how to couple the applications together. Typically in my /Application.cfc, I name my application like so:

<cfset this.name = "PUKKA_" & hash(getCurrentTemplatePath()) />

This is an easy way to guarantee a unique application name and saves me from having to worry about changing it from development to production. I picked this tip up from a Model-Glue user somewhere.

As a result of this per-directory unique name, I started by making my API code base a subdirectory of my main app: /api. Since it was a child, it would automatically leverage the same Application scope which solved objective #1. Giving them separate virtualhosts was just a matter of pointing the API vhost at the subdirectory. I also used a mod_rewrite rule to prevent access to the /api subdirectory from the parent app to enforce my logical separation:

<VirtualHost www>
...
RewriteEngine on
RewriteRule ^/api/(.*) / [NC,R=permanent]
...
</VirtualHost>

This lets me access my API separately from the main application. But requests directly to the API were still creating sessions and transmitting jsessionid/CFID/CFTOKEN cookies. My application does a little bit of work onSessionStart() so I wanted to eliminate that. My concern was disabling session management in the subdirectory Application.cfc might disable session management overall. I was also worried that if I went from www to api and back to www that my session would disappear. Not a huge concern in production but for developers during testing it would be an inconvenience.

Once in awhile, you just have to run a test to see what happens, so that’s what I did. I created an Application.cfc in my subdirectory that extends the root Application.cfc. It looks like this:

<cfcomponent displayname="Remote API" output="false" extends="/CORE_MAP/Application">
<cfset this.sessionManagement = false />
</cfcomponent>

My first attempt failed with a conflict with loginStorage = “session”. The application.loginStorage is used to control where CFLOGIN data is stored about the user. It didn’t jive to set that to session and then disable session management. In my case, I had eliminated my CFLOGIN dependency previously so I could just rip it out. The next request worked! But were there still sessions being created?

The Tests

I used two files and the CF8 server monitor to confirm this was doing what I thought it was doing. First the two files, /www/test.cfm and /api/test.cfm:
<cfdump var="#session#" expand="false" />
<cfdump var="#application#" expand="false" />

Starting by logging in to my application first, /www/test.cfm reported what it should always report, my fully populated Session and Application scopes. /api/test.cfm reported what I hoped I would see: the session scope was an empty struct and the Application scope had my shared ColdSpring bean factory. No jsessionid and according to Firebug, no cookies being passed. I loaded up CF8 Server Monitor and loaded up the api with IE7 (my previous requests were using Firefox). The active sessions count continued to show only one session! Success!

Frankly, I’m a little bit surprised by this. It seems like something you would hope “just works” but never does. :)

Results

There were a few things about the above setup that I didn’t like. The API codebase was actually a subdirectory of my www app; that meant every time I rev either application, I would have to deploy and restart both. Due to the REST API I’m building, I also need to allow PUT and DELETE requests in addition to POST and GET, which I would rather not enable as part of my main VirtualHost. My final configuration solves these issues and feels cleaner:

  • Separate subversion projects for www and api
  • Separate virtualhosts pointing to completely separate directories
  • The /api/Application.cfc still extends /CORE_MAP/Application
  • No mod_rewrite required in www app

Another problem I had, exacerbated by this setup, was the way my Coldspring bean factory was instantiated. I pilfered this original code in my onApplicationStart() from a Model-Glue tutorial before I really knew what I was doing:

<cfset application.cs.loadBeansFromXmlFile(expandPath("config/beans.xml"), true) />

The problem is when the API is hit before the bean factory exists. The expandPath fails because it tries /api/config/beans.xml which doesn’t exist. I actually have this same problem with Model-Glue sub-applications where, upon reinit, I must hit the top-level application first or else they fail with the same expandPath issue.

The solution is simple: change from a relative path to a mapping. I’m already using my CORE_MAP mapping in the EXTENDS statement (which is replaced dynamically at deployment by Ant) so why not use it in my expandPath too?

<cfset application.cs.loadBeansFromXmlFile(expandPath("/CORE_MAP/config/beans.xml"), true) />

In retrospect there wasn’t really much to worry about. I’ve been using Application.cfc/cfm in a similar way for so long I had no idea if a new approach would work. The good news is that this approach is quite flexible and lets you do a lot with a single, shared Application scope.

1 Comment

  1. Orange is my favorite color » Blog Archive » Building RESTful Web Services said:

    on November 20, 2008 at 2:44 pm

    [...] been working on building a RESTful web service with ColdFusion lately and there isn’t much information out there. Lots of people talk about [...]

{ RSS feed for comments on this post}