How ColdFusion CreateObject() Really Works With Java Objects
I use ColdFusion's CreateObject() method at least a dozen times each day to create wonderful Java objects like String Buffers and File Input Stream. Until recently, I have never really thought about how it actually works - I have just accepted that the black magic under the hood did the job properly. But then, just the other day, I discovered that you could instantiate multiple Java instances off of the same Java class returned by CreateObject():
<!--- Get the string buffer class object. --->
<cfset objStringBuffer = CreateObject(
"java",
"java.lang.StringBuffer"
) />
<!---
From the string buffer class object, instantiate
two sepparate instances of the Java string buffer.
--->
<cfset objDataOne = objStringBuffer.Init() />
<cfset objDataTwo = objStringBuffer.Init() />
When I saw this, I realized that I had no idea how ColdFusion's CreateObject() functionality really worked. After some posting and some back and forth comments, Sammy Larbi, Rupesh Kumar, and Sean Corfield have really helped to clear things up:
Apparently, when you call ColdFusion's CreateObject() method to create a Java object, ColdFusion is returning a proxy object to that Java object, not the Java object itself. This proxy object allows you to reference static properties and invoke static methods of that Java class without actually having to instantiate the full Java class. This is good for memory usage and for performance (object instantiation is a costly process).
If you call Init() on that proxy object, it then instantiates the Java object with the given arguments and returns a proxy object wrapped around this new instance (I suspect from the Init() example above that it must create a new proxy object otherwise you probably wouldn't be able to call Init() twice on the same ColdFusion proxy). Similarly, if you attempt to call a non-static method of the Java class, the proxy object will, behind the scenes, call Init() on the Java class (passing no arguments), and use that as the target class going forward.
Of course, this is what I think I understand. This may contain errors and if I get feedback on this, I will update the graphic as necessary. Thanks to all who helped me get this far.
Want to use code from this post? Check out the license.
Reader Comments
Thathow I understood it to be after Rupesh's and Sean's comments.
Awesome. Thanks for your help and feedback.
You can search "Cold" word in http://searchcode.tm-sol.com/. might give you some examples also.
found good examples as well.
How do you free the memory used by this object on the coldfusion server?
@Dan,
You don't have to - the garbage collection will handle that for you.
you can also create WebService objects this way.
i.e.:
<cfscript>
dynamicsWS = CreateObject("webservice", "http://192.168.0.100/DynamicsGPWebServices/DynamicsGPService.asmx?WSDL");
</cfscript>
<cfinvoke webservice="#dynamicsWS#" method="GetCompanyList" refreshWSDL="yes" timeout="30" returnvariable="result">
</cfinvoke>
This creates WebService object in your ColdFusion instance (available from administration panel -> webservices) and this object "lives" in there for about 10 (gets deleted if not used).
So I never took CreateObject() for granted :)
@Dmitry,
Word up - createObject() can do a whole bunch of things. It's probably one of the best updates to the language.
Ben, sheds some light on the subject for me. I've recently ventured into a shared hosting enviroment to do some outside work, and quickly ran into the java object disabled issue.
I'm not much of a programer, more a manager and integrator and struggling to find a workaround for some open source I'm using.
Thanks for the overview.
@Scott,
Glad you liked it. I'm sorry to hear that your Java objects have been disabled on the host. That can definitely get annoying :(
One quick point: I'm less certain that "object instantiation is a costly process" - object instantiation is strongly optimized on the JVM, and even reflection has far less cost than before.
IMO, the reason for the init pattern is to allow access to statics with less hoops; though this creates a somewhat schizophrenic API.
Nitpicks aside, thanks for another clear and lucid explanation of one of the details of the CF platform. Your blog has been extremely helpful to me as I learn!
I know this is a bit of an old thread, but I recently discovered something relevant. The init() function call on CF proxy objects is not thread safe.
Recently we began having random documents inserted into the incorrect collection on our MongoDB. We tracked it down to our own Mongo driver which leverages the Mongo Java driver as a singleton. However, in concurrency situations, creating new instances of our java class using the above method caused us sometimes to get the wrong instance back.
For example:
Process A calls init("collection1")
Process B calls init("collection2")
Both calls return CF proxy objects with the same instance of our CFMongo class pointing to collection2.
So if you are going to cache your static class in a shared location, like the application scope, you need to only call init() with an exclusion lock. In our case we chose to use CreateObject() each time passing the mongo driver singleton into the constructor.
Hi Ben, interesting post.
It looks like I'm running into the CF proxy object as an issue on a routine I'm writing.
I'm importing the Amazon Web Services SDK for Java into a CFC and to initialise the S3 Client you first have to create an authentication object. However passing this object (which is created correctly) to the init() function throws an "An exception occurred while instantiating a Java object. The class must not be an interface or an abstract class" error.
I'm guessing that because this isn't a true Java object the second class doesn't like it?
Anyway here's the code:
Any ideas how to instantiate a real Java object?
Thanks
James
Ignore my earlier post, it appears that the Amazon SDK requires some more third party Jar files in the class path than it comes with, namely httpclient-4.1.1.jar if you're having similar problems.