Viewing by month: April 2008

Apr 24 2008

Getting a javax.sql.DataSource for a ColdFusion Datasource Name

On the DALNET #ColdFusion channel, a user needed to get a javax.sql.DataSource instance for use with a Java library, and wanted to use one of their configured ColdFusion datasources.

I've got a bit of code around for this kind of stuff, and thought it'd be handy to blog it in case anyone else needs to Google for the same type of functionality. The following UDF will do it, but it does make use of the largely undocumented ServiceFactory that underpins ColdFusion.

<cffunction name="getDataSource" output="false" hint="Gets the raw javax.sql.DataSource implementation for a given CF datasource name.">
   <cfargument name="dsn" />
   
   <cfset var svc = createObject("java", "coldfusion.server.ServiceFactory").DataSourceService>
   <cfset var ds = svc.getDatasource(javacast("string", arguments.dsn)) />
   
   <cfreturn ds />
</cffunction>

0 comments - Posted by Joe Rinehart at 12:31 PM - Categories: Joe Drinks Java | ColdFusion MX

Apr 24 2008

Flex and ColdFusion: Returning error types

When I write a service layer for a Flex application (or other types of clients), I often make heavy use of <cfthrow> tags in my service CFC's.

For example, if I were building a Flex shopping cart and a user wants to do service.placeOrder(), but placeOrder() checks user authentication status and user.isLoggedIn() is false, I need to indicate not just a failure but what went wrong back to the client.

In pure ColdFusion, I'd do this with a custom type attribute on <cfthrow>:

<cfthrow
   type="cartService.userNotLoggedIn"
   message="User must be logged in!"
/>

Flex, however, doesn't get to see the value of the type attribute: it just gets a plain exception without much information other than the value of the 'message' attribute, which I like to leave human readable and populate with helpful text.

To send back the "type" of exception, set both the type and the errorCode attributes:

<cfthrow
   type="cartService.userNotLoggedIn"
   errorCode="cartService.userNotLoggedIn"
   message="User must be logged in!"
/>

Now, in your Flex app, you can test what went wrong by testing the faultCode property of the fault, which'll be populated by the errorCode's value:

private function placeOrderFaultHandler(info:Object):void
{
   var fault:Fault = FaultEvent(info).fault;

   switch (fault.errorCode)
   {
      // These work great as constants on a CartServiceFaults class.
      case "cartService.userNotLoggedIn":
         dostuff();
         break;
   }
}

4 comments - Posted by Joe Rinehart at 10:27 AM - Categories: Flex and ColdFusion | Best Practices

Apr 18 2008

MG3 and CF Framework Performance Benchmarks

Whenever I work on the core of a Model-Glue version, I use a suite of sample applications and JMeter tests to make sure I'm not wrecking anything in a performance or resource context. I measure against both prior versions of MG and other frameworks, making sure nothing anomalous is sneaking into MG by watching for spikes in the numbers. I'm not trying to "compete" as much as I'm just looking for comparable benchmarks for Model-Glue, and performance profiles of the other frameworks are my best bet.

For running these tests, I maintain a set of applications (Model-Glue, Fusebox, Mach-ii, and now ColdBox) that are the simplest possible request - they don't even show any output. For Mach-II and Model-Glue, no listener functions are run. For ColdBox, it's just the minimum required handlers and a simple "main" handler that adds one view.

Basically, it's a test of how well each can spin up its equivalent of a request context, execute it, render one empty view, and shut down.

This morning, I slaved my PC to running these tests, which ramp up from 25 to 100 simultaneous users, each making 100 back-to-back requests. The following chart shows the results:

Coming back soon...getting an optimized application shell for ColdBox from its team.

Note: don't use these numbers for choosing a framework. Application code is much more likely to be a bottleneck than the framework!

If anyone else would like to try these out (or if any framework authors would like to make sure I've made the sample apps as simple as possible!), I've attached the Eclipse project I use for all of this to this entry. Maybe it'd be a good RIAForge project?

9 comments - Posted by Joe Rinehart at 10:27 AM - Categories: Model-Glue | Frameworks

Apr 15 2008

Sharing MG ColdSpring Beans With Other Applications

When you write a Model-Glue application, it both manages ColdSpring for you and gives you a place to define beans.

Eventually, though, you may want to use some of these beans outside of your Model-Glue application but still inside the same application scope. You may be writing a Flex application or simply creating a common pool of beans / services all of your apps can share.

To do this well, you'll need to use what are known as "parent bean factories." Simply put, you'll first create your own instance of ColdSpring's bean factory then instruct your Model-Glue application to look for beans first in its internal and then in this "overaching" ColdSpring factory.

Step 1: Managing ColdSpring yourself

You'll first need to manage creation of your own instance of a ColdSpring bean factory. In your Application.cfm (easy to port to Application.cfc), you'll want to add code to create it or reload it as necessary (Note: this can be improved, and should be, with a double-checked lock).

<!--- Reload beans if not present or url.init is defined --->
<cfif not structKeyExists(application, "sharedBeanFactory") or structKeyExists(url, "init")>
<cfset application.sharedBeanFactory = createObject("component", "coldspring.beans.DefaultXmlBeanFactory").init() />
<!--- Load your common beans XML file --->
<cfset application.sharedBeanFactory.loadBeans(expandPath("/my/shared/beans.xml")) />
</cfif>

Ok. Your application now contains a DefaultXMLBeanFactory instance. You can test it out by writing a quick index.cfm with an application.sharedBeanFactory.getBean("myBean") call that'll return a bean from the XML file.

Step 2: Configuring Model-Glue

With that taken care of, we need to tell our Model-Glue application to look first in its local config/ColdSpring.xml file first, then to look in the sharedBeanFactory second.

Open up your Model-Glue application's index.cfm, and you'll see this line of code commented out (assuming it's from an MG2 application template):

<cfset ModelGlue_PARENT_BEAN_FACTORY = ??? />

Uncomment it, and change it to:

<cfset ModelGlue_PARENT_BEAN_FACTORY = application.sharedBeanFactory />

Ok, you're good to go! Beans defined in the sharedBeanFactory's XML file are now available to your Model-Glue application as well as any other applications in the same application scope.

3 comments - Posted by Joe Rinehart at 3:09 PM - Categories: Model-Glue

Apr 10 2008

"Getters and Setters" - a different CFC approach

With an ongoing debate over me stating that generic getters and setters were a bad idea, I thought I'd take a stab at an implementation that tried to fix Peter Bells's fault with them (they're verbose) without falling into problems I had with Peter's "Generic" approach:

  1. They're a direct encapsulation violation, as you have to know implementation details of the CFC to know how to use it.
  2. It's based on the magic string / magic number antipattern, where strings evaluated at runtime take on API, not data, significance.
  3. They don't produce an API that can be used by others, as an object has no external API describing its properties.

While I was accused of zealotry, the accusation was based on the assumption that I thought forcing people to write getFoo() and setFoo() is a hard-and-fast rule applicable to every application.

It's not.

I cooked up the following bit, following an idea Mark Mandel had a while back while discussing implicit setters. I can't claim any originality - over IM last night, Mark knew exactly what I was up to before I even told him what I was doing.

The "hack" results in a property manipulation API such as this, where you use <cfproperty> tags to declare properties, and only those marked as access="public" can have accessors and writers "generated" on-the-fly:

<!--- CFC --->
<cfcomponent extends="AccessorHack">
<cfproperty access="public" name="name" />
<cfproperty access="private" name"privateProperty" />
</cfcomponent>
<cfset contact.name("Hank") />
<!--- Shows "Hank" --->
<cfoutput>#contact.name()#</cfoutput>
<!--- Tosses private property error --->
<cfset contact.privateProperty("value") />
<!--- Tosses variable not defined --->
<cfreturn contact.undefinedProperty() />

What I Like About It

  1. Objects are still true objects - they have defined properties. Encapsulation and API definition are maintained.
  2. Instead of literals (set("Name")), you work against methods.

What I don't like about it

  1. The syntax is a bit weird. The equals sign is just what I see as being assignment to a property, but setFoo() doesn't have that either.
  2. There's a runtime cost associated with getting all properties up an object's inheritance chain on the first get/set, and I'd find that unacceptable in a high-load app.
  3. There's no type-checking, as I didn't want to add that cost or write the code for it.

Conclusion

It's an interesting experiment, but not one I'll put forth as a good technique for big apps.

Code

Here's the code that was used (obviously CF8-only):

<cfcomponent name="AccessorHack">

<!--- Builds a table of properties metadata that crawls up inheritance chain. --->
<cffunction name="buildPropertyTable" output="false">
   <cfargument name="table" default="#structNew()#" />
   <cfargument name="md" default="#getMetadata(this)#" />
   
   <cfset var prop = 0 />
   
   <cfif structKeyExists(md, "properties")>
      <cfloop array="#md.properties#" index="prop">
         <cfif structKeyExists(prop, "access") and prop.access eq "public">
            <cfset table[prop.name] = prop />
         </cfif>
      </cfloop>
   </cfif>
   
   <cfif structKeyExists(md, "extends")>
      <cfset buildPropertyTable(table, getComponentMetadata(md.extends.name)) />
   </cfif>
   
   <cfreturn table />
</cffunction>

<!--- Sets / gets whatever is passed directly to / from variables, as long as it's declared "public" --->
<cffunction name="onMissingMethod" output="false">
   <cfargument name="missingMethodName" />
   <cfargument name="missingMethodArguments" />

   <!--- Build table if necessary --->
   <cfif not structKeyExists(this, "publicPropertyMetadata")>
      <cfset this.publicPropertyMetadata = buildPropertyTable() />      
   </cfif>
   
   <cfif not structKeyExists(this.publicPropertyMetadata, missingMethodName)>
      <cfthrow message="#missingMethodName# is not declared as public in a &lt;cfproperty&gt; tag!" />
   </cfif>
   
   <!--- Beware: doesn't support named args --->
   <cfif structCount(missingMethodArguments)>
      <cfset variables[missingMethodName] = missingMethodArguments[1] />
   <cfelse>
      <cfreturn variables[missingMethodName] />
   </cfif>
</cffunction>

</cfcomponent>

5 comments - Posted by Joe Rinehart at 8:00 AM - Categories: ColdFusion MX | OOP | Causing Trouble

Apr 9 2008

MG3 Video Sneak Peak: Event Generation

One of my favorite features coming in MG3 is purely for developer ease of use: hit an event that doesn't exist while in development mode, and it'll generate intelligently named controllers, listener functions, and views, wiring them all together with convention-based XML.

Here's a video of Event Generations (and a bit of SES features) in action:

13 comments - Posted by Joe Rinehart at 5:01 PM - Categories: Model-Glue

Apr 8 2008

Bad Idea: Generic Getters/Setters on CFCs

Earlier today, Peter Bell posted again on using "generic" getters and setters in CFCs. He's posted about it on his blog repeatedly over the past few years, and he and I recently butted heads a bit on the Model-Glue list about why I feel they're a bad (ok, I've used much stronger terms than that...) idea.

Read more...

35 comments - Posted by Joe Rinehart at 11:13 AM - Categories: Worst Practices | Best Practices