Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with:

Instantiating Groovy Classes In The ColdFusion Context

By Ben Nadel on
Tags: ColdFusion

Disclaimer: I just started looking into Groovy so a lot of this might be dead wrong.

When I saw Barney Boisvert demonstrate Groovy at CFUNITED, I became very interested in experimenting with it. One of the first things that I wanted to try was defining classes in Groovy and then using them in ColdFusion. The problem, however, is that the Groovy context and the ColdFusion context don't exactly have bi-directional communication. When the Groovy context is opened in Barney's script (from what I am gathering), certain ColdFusion scopes are bound to the Groovy context such that the Groovy context can read from and write to the ColdFusion context. Once the Groovy context is closed, however, ColdFusion doesn't have a handle on it.

I am probably not understanding this fully, but from I have been reading, one huge benefit of Groovy is that it is lexically bound. Meaning, objects in Groovy can, more or less, access data that was defined in a given context, even after references to that context have been passed out of that context. While I am not explaining that well, this is the same lexical binding feature that makes Javascript closures so unbelievably powerful.

When I read this, I wanted to see if this lexical binding could be leveraged to create a Groovy Class Factory that could be passed into the ColdFusion context in such a way that it would provide ColdFusion with a portal back into the original Groovy context. Essentially, I wanted to create a Groovy proxy that ColdFusion could use to instantiate classes defined in the proxy's original context.

To make this as flexible as possible, I wanted to use some sort of Reflection in which the Groovy Class Factory could create instances based on the given class name without having to hard code a bunch of different constructors. Essentially, I wanted to somewhat mirror ColdFusion's CreateObject() method and allow people to do something like this:

groovyFactory.get( "CLASS_NAME" ).init( arg, .... N )

To do so, I created a factory class that had a single method, get(). This get() method would then create the Class object based on the given class name and store it in a class instance proxy object. This class instance proxy object then had a single method, init(), which would create new instances based on the given constructor arguments. I am sure there is a MUCH better way to do this, but I am wicked new to Groovy, so my init() method is a bit of kludge; it can take only up to 10 arguments which it then translates into a dynamic-length array which is used as the argument collection in the target class' instantiation method.

A bit of a hack, but it totally works. The top of this code sample defines the Groovy context. The ColdFusion code that leverages this context is as the bottom:

  • <!--- Import the CFGroovy tag library. --->
  • <cfimport prefix="g" taglib="./cfgroovy/" />
  •  
  • <!---
  • The Groovy script tag caches the Java loader in the Server
  • scope. For testing purposes, we are going to clear it out
  • (as I think it caches the available classes??).
  •  
  • NOTE: This might be completely wrong!!
  • --->
  • <!--- <cfset structDelete( server, "cfgroovy" ) /> --->
  •  
  •  
  • <g:script>
  •  
  • <!---
  • Define the GroovyFactory class. This is the class that
  • will allow ColdFusion to instantiate Groovy-bound classes
  • once back inside the ColdFusion context. This is because
  • Groovy is lexically-bound, meaning that it has access to
  • the variables / class in the context in which it was
  • defined. As such, once an instance of this is passed out
  • of Groovy, it will still have access to this Groovy
  • context.
  • --->
  • class GroovyFactory {
  •  
  • <!---
  • This method returns a Groovy class proxy using the
  • given class name such that new instances can be
  • instantiated in such a manner:
  •  
  • GroovyFactory.get( className ).init( arg, ... arg );
  •  
  • Basically, I just wanted to be able to seperate the
  • class name from the list of constructor arguments.
  • --->
  • public get( String className ){
  • return(
  • new GroovyFactoryInstanceProxy(
  • java.lang.Class.forName(
  • className,
  • false,
  • this.getClass().getClassLoader()
  • )
  • )
  • );
  • }
  •  
  • }
  •  
  •  
  • <!---
  • Define the Groovy instance proxy class. This class is a
  • proxy for the constructor of the target class. It
  • containst the class definition internally such that the
  • init() method can be called with a list of N arguments.
  • --->
  • class GroovyFactoryInstanceProxy {
  •  
  • <!--- Store the class internally. --->
  • private def targetClass = null;
  •  
  • <!---
  • This is the constructor for the proxy. It simply
  • stores the target class definition for use in the
  • init() method call.
  • --->
  • public GroovyFactoryInstanceProxy( Class targetClass ){
  • this.targetClass = targetClass;
  • }
  •  
  • <!---
  • This is the actual factory method for the current
  • target class. It takes N arguments (max of 10)
  • arguments, which it converts into an array that will
  • be used to call the target classes constructor
  • (using the reflection method - newInstance()).
  •  
  • NOTE: I am using 1-10 here because I could not yet
  • figure out how to have a variable number of arguments
  • that did NOT require them to be an array when called
  • from ColdFusion.
  • --->
  • public init(
  • Object a1 = null,
  • Object a2 = null,
  • Object a3 = null,
  • Object a4 = null,
  • Object a5 = null,
  • Object a6 = null,
  • Object a7 = null,
  • Object a8 = null,
  • Object a9 = null,
  • Object a10 = null
  • ){
  •  
  • <!---
  • Create a local collection of arguments that we
  • will use during our constructor call on the
  • target class.
  • --->
  • def arguments = [];
  •  
  • <!---
  • Loop over each argument that was passed to this
  • method, and, if it is NOT NULL, then add it to
  • the collection of arguments we will use to
  • instantiate the target class.
  •  
  • NOTE: We are using an Closure here who's first
  • argument is implicit stored in the variable, "it".
  • --->
  • [ a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 ].each({
  • if (it){
  • arguments.add( it );
  • }
  • });
  •  
  • <!---
  • Return a new instance of the target class using
  • the mapped arguments array.
  • --->
  • return(
  • this.targetClass.newInstance( arguments.toArray() )
  • );
  • }
  •  
  • }
  •  
  •  
  • <!---
  • Store the Groovy Factory instance in the ColdFusion
  • variables scope so that we can now access this ENTIRE
  • Groovy context (via the Factory) from ColdFusion.
  • --->
  • variables.groovyFactory = new GroovyFactory();
  •  
  •  
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  • <!--- Groovy Class Definitions ------------------------ --->
  • <!--- ------------------------------------------------- --->
  • <!--- ------------------------------------------------- --->
  •  
  •  
  • class Person {
  •  
  • private def name = "";
  • private def hair = "";
  • private def gender = "";
  •  
  • public Person(){
  • <!--- Nothing to do here. --->
  • }
  •  
  • public Person(
  • String name,
  • String hair = null,
  • String gender = null
  • ){
  •  
  • <!--- Store properties. --->
  • this.setName( name );
  •  
  • if (hair){
  • this.setHair( hair );
  • }
  •  
  • if (gender){
  • this.setGender( gender );
  • }
  • }
  •  
  • public String getName(){
  • return( this.name );
  • }
  •  
  • public Object setName( value ){
  • this.name = value;
  • return( this );
  • }
  •  
  • public String getHair(){
  • return( this.hair );
  • }
  •  
  • public Object setHair( value ){
  • this.hair = value;
  • return( this );
  • }
  •  
  • public String getGender(){
  • return( this.gender );
  • }
  •  
  • public Object setGender( value ){
  • this.gender = value;
  • return( this );
  • }
  •  
  • };
  •  
  •  
  •  
  • class Relationship {
  •  
  • def private Person person1;
  • def private Person person2;
  •  
  • public Relationship(
  • Person person1,
  • Person person2
  • ){
  • this.person1 = person1;
  • this.person2 = person2;
  • }
  •  
  •  
  • public String toString(){
  • return(
  • (
  • person1.getName() +
  • (
  • person1.getHair().length() ?
  • (" (" + person1.getHair() + ") ") :
  • ""
  • ) +
  • " is dating " +
  • person2.getName() +
  • (
  • person2.getHair().length() ?
  • (" (" + person2.getHair() + ") ") :
  • ""
  • ) +
  • " ... awesome!"
  • )
  • );
  • }
  •  
  • };
  •  
  • </g:script>
  •  
  •  
  •  
  • <!--- Create a girl. --->
  • <cfset sarah = groovyFactory.get( "Person" ).init(
  • "Sarah",
  • "Brunette",
  • "Female"
  • ) />
  •  
  • <!--- Create a boy (using a slightly different syntax). --->
  • <cfset ben = groovyFactory.get( "Person" ).init()
  • .setName( "Ben" )
  • .setHair( "Brunette" )
  • .setGender( "Male" )
  • />
  •  
  • <!--- Create a relationship. --->
  • <cfset relationship = groovyFactory.get( "Relationship" ).init(
  • sarah,
  • ben
  • ) />
  •  
  • <!--- Output the relationship string. --->
  • <cfoutput>
  • #relationship.toString()#<br />
  • </cfoutput>

As you can see, I have my two Factory / Proxy classes as the top of the Groovy context, followed by a Groovy class definitions for Person and Relationship. After the Groovy Factory class is defined, an instance of it is created and stored back into the ColdFusion Variables scope. Once outside of the Groovy context, ColdFusion then uses this Groovy Factory instance (stored in the implicit Variables scope) to create two Person instances and a Relationship instance.

Running the above code, we get the following output:

Sarah (Brunette) is dating Ben (Brunette) ... awesome!

As you can see, by using the Groovy Factory, the ColdFusion context is successfully instantiating Groovy classes from outside of the Groovy context. Since the Groovy Factory class was defined in the Groovy context, it can reference that Groovy context even once outside the Groovy context. As such, once it is passed into the ColdFusion context, it can act as a tunnel back into the original Groovy context.

I'm not sure where I'm going with this; but, I really like the idea of being able to create Groovy classes from within ColdFusion. If for no other reason, it will make experimenting with Groovy a lot easier.




Reader Comments

Don't forget that you don't need getters/setters in Groovy unless you want custom behaviour (like setters returning 'this'). So you can strip all the getters from your code and just use the properties. And you don't have to call them either, you can create a person like this:

[cfset person = ... /]
[cfset person.hair = "Brunette" /]

and it'll invoke the setHair method.

Clearing server.cfgroovy will, in fact, reset the CFGroovy context, but it's rarely necessary. CFGroovy automatically reloads it's context for each new script body, so when you change your script, you'll start from scratch. The only reason you'd need to wipe server.cfgroovy is if you needed to load new JARs or something, but you'd have to restart your container as well, which would make it moot.

And don't forget things like Person.enterRelationship(otherPerson) to create relationships between objects, rather than doing it manually.

I wrote up a blog post expounding on some related topic that didn't seem to fit a comment very well. It's at http://www.barneyb.com/barneyblog/2009/09/17/groovy-objects-in-cfml-a-la-ben/

Reply to this Comment

Just making sure you know that the getters and setters are actually implicit in Groovy, so as long as you have the

def Name

groovy would create

getName()
setName( 'Ben' )

for you.

Also, if you wanted to set "Name" on init, you could do

new Person( [ name: 'Ben' ] ) (and no need for a constructor either...it automagically sets 'Ben' to the name property.)

Reply to this Comment

@Barney,

Ah, good points. Yeah, I had the server variable in there for a while, but then I commented it out and found that it was working fine. I left it in the code example, just for... documentation I guess. I think at first, I thought maybe it was causing some method signature errors, but in the end, it was not.

I will check out your blog post, thanks.

@Gareth,

Thanks, I'll check out that link.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.