Those of you who remember CF Rhino will recognize the name for my latest little project. I whipped up a small proof-of-concept integration for Groovy into CF apps tonight while playing with some classloading issues within Groovy itself.
Groovy has a number of advantages for this type of integration of JavaScript, the biggest one being that Groovy IS Java, and carries Java's semantics almost perfectly. So unlike with Rhino, no major conversions are needed moving in and out of Groovy, which makes things enormously easier. CF still brings it's special blend of "insane" to the party, but it works quite well overall. Definitely has more real-world potential than CF Rhino ever did.
CF Groovy is not a whole framework like CF Rhino was, it's really just a proof of concept for integrating the two together and sharing resources. You can get the demo (which includes the engine via svn:externals) from Subversion at https://ssl.barneyb.com/svn/barneyb/cfgroovy/trunk/demo/. Just check it out to a directory named 'cfgroovy' in your webroot, drop the groovy JAR from the 'groovyEngine' subdirectory into your /WEB-INF/lib folder, and you should be done. You can see it in action at the demo page, though without source to browse, it's not very interesting.
Also, unlike CF Rhino, there's no dependencies on the internals of ColdFusion (the Adobe product). To do some of the bridging to JavaScript, I had to invoke internal APIs of ColdFusion, so it wouldn't run on BlueDragon, Railo, etc. I built CF Groovy with Open BlueDragon and Railo 3 on Tomcat simply because I didn't have a CFML runtime on my box, and CF is like 400MB while oBD is 11MB and Railo is 25MB.
Unfortunately, all three runtimes have bugs that prevent them from working as expected.
Railo was the worst: it has some recursion issue with the JEE.include() call. It also doesn't seem to implement java.util.Map with it's struct implementation, and throws on missing keys rather than returning null.
Open BlueDragon initializes ResultSets from manually assembled queries (and possibly "real" queries) with the row pointer pointed at the first row, rather than before the first row as the spec says it should be.
ColdFusion doesn't create java.util.Date objects with the createDate built in, so trying to call ResultSet.getDate() throws a ClassCastException. Using ResultSet.getObject() and letting the ducks work their magic fixes this issue. It's also 15-35 times larger than the others.
Except for the download size issue, I've got conditionals to cover all the cases in the app, so it should be checkout and run on all three platforms. I haven't tested NA BD, CF7-, or Railo 2. I don't know why they wouldn't work, though, since I'm just doing standard Java stuff. I'm not doing any version conditionals, only platform ones, just in case.
Very cool. Is this something that, given some more work, you plan to use for real projects?
Dan,
I'm not sure. As it stands right now, it's a viable way to embed groovy right inside your apps. As for turning it into an whole-app framework, I'm not sure about that. The real goal was to deal with some classloader issues so that I can use Groovy direct from the URL (via GroovyServlet), and built entire applications in Groovy with no CF dependencies. CF could still be used for the views, of course, but that's a secondary decision.
For the direct embedding, it has some potential for storing groovy scripts in the DB and executing them dynamically for business rule configuration. I.e. you can change certain business rules without having to change the CF code, you just update the Groovy script in the DB. That sort of arrangement is difficult with CF (less so on BD), and the CF syntax is less friendly. I'll probably end up using it for that in a couple apps.
hi barneyb
you are absolutly right, a railo struct does not extends java.util.Map.
that's because railo's struct is optimized for the railo enviroment, it neeeds no interpetation, like a map in cfmx needs.
why you access a struct via reflection (struct.get(key)) instead of native cf access (struct[key]), every refection access is 1000 times slower than a native access! reflection access on coldfusion object is always the worst decision.
on java level railo can't be 100% similar to cfmx, only cfmx can this.
what is exactly the problems with include?
greetings michael
Michael,
Regarless of what my CFML code is (struct.myKey, struct["myKey"], or struct.get("key")), it seems the bytecode should be equivalent. That's what compilers are for, no? However, I wasn't talking about a CFML reference, I was talking about using a Railo struct within a "pure" Java context. Within Groovy, Maps have special syntax-level support which doesn't quite work with Railo structs, because they're not Maps. They were Map-like enough to support the syntax constructs, but because they throw an error on a missing key instead of null, it basically make it moot (no "elvis" operator, have to add a bunch of try..catch, etc.). Both oBD and CFMX use Maps to implement CF structs, so it's not a CFMX-specific feature, it seems to be a Railo-specific non-feature.
RE the include, this line was the offender:
pageContext.getServletContext().getRequestDispatcher(path).include(pageContext.request, pageContext.response)
The 'pageContext' variable is the result of calling getPageContext() within CFML, though that line was actually executed from within Groovy/Java, not Railo. The 'path' variable was set to "/cfgroovy/sample_include.cfm". It ended up with infinite recursion, trying to include "index.cfm" over and over again, rather than "sample_include.cfm". I didn't dig in too deeply, I just saw repeated includes of "index.cfm" until I killed the JVM, and so added a conditional so it didn't run on Railo in the demo.
[...] wanting to create little ad hoc Java classes in my CF apps in a common occurrence for me. With my CF Groovy project, it's both possible and very easy. No Java, no compiling, no classloading [...]