Instantiating Groovy Classes In The ColdFusion Context

Posted September 17, 2009 at 9:49 AM by Ben Nadel

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

Sep 17, 2009 at 12:49 PM // reply »
25 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/


Sep 17, 2009 at 12:54 PM // reply »
110 Comments

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.)


Sep 17, 2009 at 12:54 PM // reply »
110 Comments

Damnation...beat me to it :)


Sep 17, 2009 at 12:57 PM // reply »
110 Comments

One more thing...I found this series to have lots of useful information about implementing groovy
http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=practically+groovy
from the basics all the way up to the more complex stuff


Sep 17, 2009 at 1:28 PM // reply »
11,238 Comments

@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.


Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools