This document was prepared by Joe Rinehart for Alagad, Inc.
Building a CFML project with a Groovy model is a productive choice. In my experience, Groovy just “feels” better for modeling a domain, and its complimentary tooling (Hibernate, Spring) “just works.” It’s a complicated beast, however, and having a guide to setting it up is handy. I’ve spent a good deal of time, both on my own and at Broadchoice, learning both the concepts of JEE Web Applications and the ins and outs of setting up these hybrid projects. This document is intended to walk you through the concepts and tools used in such a setup. Actually setting up the environment is individual to your situation, and something we’ll work on together. A lot of this is still rough around the edges. Eventually, I’d like to document a “template” of two different models of Groovy+Spring+Hibernate+CFML applications. Any attempt to do so now, however, would be obsolete the moment the environment was complete: each time through is a learning experience!
As you move from pure CFML to a Groovy/CFML environment, there’s some new ground to cover. At first glance, the just the terminology around JEE applications is intimidating. Terms like “descriptors,” “application contexts,” and “transaction demarcation” are foreign to ColdFusion developers. With a little bit of general knowledge and a smattering of core foundation code, it’s not hard to move past this into a productive state, even if the entire team doesn’t quite understand what’s going on under the hood. That’s the beauty of object oriented programming: not every coder needs to know how everything works! Let’s start off by walking through the general knowledge we’ll need to develop a hybrid application.
Concept 1: JEE applications vs. CFML applications
The largest shift in mentality is the shift away from the idea of a “ColdFusion” application. In the Java world, when you want to start a new Web project, you create a new JEE application. In the ColdFusion world, we use one JEE application (the CFML engine itself), writing multiple ColdFusion applications, each denoted with its own Application.cfc or Application.cfm file.
Why does this matter for a Groovy / CFML project?
It actually has nothing to do with Groovy, but with our underlying support frameworks. The easiest way to manage Spring and Hibernate in a Web environment is to use JEE application mechanics to load Spring and deal with a few quirks of Hibernate. It’s not an issue with the frameworks, it’s simply how they were designed: to take advantage of the most common way of writing Web applications in the pure Java world.
What can we do about it?
- Be Enterprisey
If you’re working on a large application, it’s likely to be the only one running on its CFML server. In fact, it may be the only one running across a cluster of CFML servers. If you’ve got this luxury, using the built in utilities to manage Spring in a manner that automagically binds one “application context” (we’ll define that in a bit!) to a JEE Web application is the way to go.
- Manage things yourself
If you need to run more than one application, life is more complicated. Through Application.cfc, it’d be possible to use onApplicationStart to create and configure the filesystem-based implementation of the Spring XMLApplicationContext, placing the Spring factory in the Application scope. This’d be just like creating your own ColdSpring factory.
The second piece of the puzzle deals with Hibernate sessions. We’ll cover the “why” in a bit, but we need to hold what’s known as the “Hibernate session” open until the request ends. In a Spring Web application (Option 1), there’s a really simple servlet filter that can be used. In this setup, however, you don’t have that option. Instead, it should be possible to move the closure of the session into Application.cfc’s onRequestEnd method with the same result.
I haven’t done this in practice, but it’s conceptually possible. Andrew Powell (UniversalMind) has demonstrated such a setup, so it’s definitely workable. One thing I’d like to research is a setup (using Model-Glue) that uses two Hibernate sessions: one in the listener method phase that is a normal read/write session, and one in the view phase that is read-only, using an optimized isolation level for increased performance (not to mention it’d stop anyone doing dao.save( instance ) from within a view!).
Concept 2: Stateful Persistence
In the ColdFusion world, we’re used to a really simple way of working with databases. In the Java mindset, there “is no database.” That pretty much blows what we know out of the water. Under the idea of stateful persistence, you’re not working with records in a database. Instead, you work with objects, changing their state (values of their properties). When you’re ready, you ask “something” to save this state, whether it’s a hand-rolled Data Access Object or a full-bore ORM. In our case, we’ll be working with an ORM (Hibernate), so we’ll be focusing on its concepts. The first concept of working with Hibernate to understand the the idea of the session. In Hibernate terms, a session (not ColdFusion session, but Hibernate session!) is a “unit of work” against the database. It’s not necessarily a single CRUD operation: it could be the saving of a batch of instances, the deletion of one, or a combination of the above. Either way, all work is done within a session. When you’re ready to roll, the session is committed as a transaction. Yep, it’s complicated. Hibernate tutorials teach you to get the current session, do work, then commit and close, all manually. It’s good to know how this all works, but nobody wants to deal with it constantly. Luckily, a real-world framework (Spring) wraps all this rigamarole into utility classes and convenience Abstract classes that let you concentrate on writing simple DAOs to use Hibernate w/o having to worry about the mechanics of using it. In other words, Spring lets you write your DAO’s save() or HQL-based list() containing just your logic, not Hibernate-management code.
Concept 3: Development Environment
Your development environment is going to change. You’re likely to end up with 3-4 Eclipse projects:
- A Java project. The Groovy plugin has issues compiling projects that have Java code that relies on Groovy code that relies on Java code (or any combination thereof). Basically, one has to compile before the other, which can make life….interesting. I typically run a separate Java project that contains some foundation code as well as any Java interfaces to which I’ll need to apply Spring AOP.
- A Groovy project. This is where most of my code lives. My model, services, and implementations of any interfaces declared in the Java project live here. It’s a lot more fun to write in Groovy than in Java.
- A CFEclipse project. A standard Model-Glue project living on my CFML server, with the exception that I don’t have a /model directory. Instead, I use a .jar export of the Java and Groovy projects into ColdFusion’s WEB-INF/lib.
- (Optional) A ColdFusion Web Application project. I’ve only done this with Railo, but it’s possible to create a JEE Web project within Eclipse that is your CFML server itself, mimicking a standard JEE application. This lets you start / stop and publish to the server from within Eclipse. It’d likely replace project #3 entirely, as your CFML source code would just be part of the JEE application itself.
This setup does have the issue of requiring you to publish a .jar to your CFML server and having you restart the server any time you make a change. I thought I’d hate it at first, but it really makes you separate your work nicely: if you’ve written good unit tests for your Java / Groovy projects, you shouldn’t be deploying to your CFML server until things are ready to roll in the first place! On the other hand, I’m often guilty of just “sne
aking in” one little change, compiling, restarting ColdFusion, then finding out my little change breaks the whole works – and if I had unit tested in the first place, I wouldn’t have just wasted the 32 seconds it takes to start JBoss on my MacBook Pro. Just a “test first!” side note.
Concept 4: The Spring BeanFactory
We’re going to use Spring to manage Groovy-based services. It’ll work like ColdSpring, allowing autowiring of these services to your controller. There’s nothing much to say here: it “just works.” Spring’s a big beast though, and it’s worth reading a book. There’s simply too much goodness in there to describe in this document: everything from really easy JMS implementation to customizable security applied via simplified AOP. Buy “Spring in Action,” second edition. Read the core concepts and persistence (Hibernate) chapters. Skim whatever else is applicable. That is all.
Concept 5: Overall System Architecture
“But what does it all look like put together?” I’m sure someone’s been asking this for a while, but we had to settle a few other issues. Let’s start from the ground up.
- A Spring ApplicationContext acts as a bean factory. It contains Groovy-defined services and other utility beans, such as Groovy-based DAOs. These Groovy-based DAOs implement Java-based interfaces, allowing you to use the Spring-based Hibernate conveniences that tell Hibernate when to start / commit a transaction. That’s because the conveniences rely on Spring AOP, which needs a true Java (not Groovy) interface. It sounds painful, but one single Java interface is all that’s really needed (defining a base DAO contact for all others to inherit).
- A ColdSpring bean factory “wraps” the Spring application context, using it as a parent bean factory. I’ve got the code for this, and setting it up isn’t hard: you just twiddle your Model-Glue’s application’s index.cfm a bit.
- You write tests for your domain objects (beans) in Groovy.
- You write your domain objects in Groovy and test them, using Hibernate or JPA annotations to guide Hibernate in their persistence.
- You write tests for your persistence layer in Groovy.
- You write your persistence layer’s interfaces in Java then implement them in Groovy.
- You write tests for your service layer in Groovy.
- You write your service layer, coordinating your domain objects and persistence layer, in Groovy.
- When all tests pass, you export the Groovy and Java projects to WEB-INF/lib and restart your CFML engine.
- You then write Model-Glue controllers that use the service layer, and business as usual begins.
Okay, we’ve covered the new foundation knowledge we’ll need. It’s time to meet the a few new characters in our hybrid project show that we haven’t yet talked about.
I thought I knew what Spring was until I used it. It’s not just “everything ColdSpring does, but for Java.” It’s more like everything Java should come with in the first place. It’s a Swiss-army knife of helpful functionality, providing modules that provide bean factories, persistence helpers, testing helpers, an MVC framework, a security framework, JMS simplification, Flex integration, and a whole lot more. Buy “Spring in Action,” second edition. It is essential.
Industry-standard ORM framework for Java. You can use it and forget the database exists. However, it’s a complicated beast, but with good reason. “Java Persistence with Hibernate” is a book well worth the investment, as it covers both the concepts behind ORM (showing why Hibernate is as complicated as it is!) and how to use the thing in the first place.
TestNG is my preferred unit testing framework for Groovy. It’s a lot like CFCUnit or MXUnit. Spring provides TestNG conveniences that will allow us to load our entire application context inside our unit tests. It also provides a “transactional” convenience base test class that’ll make any work we do against the database in our tests function as a transaction that gets rolled back when the test completes (or fails!), meaning that our tests won’t pollute our development databases. TestNG + Spring rocks.
Groovy Eclipse Plug-In
The Groovy Eclipse Plug-In allows you to add a Groovy project nature to any Java project. It mostly works, but can sometimes go nuts. Cleaning your project usually helps. Its code hinting is utterly useless, primarily due to Groovy’s dynamic nature. This is similar to why CFC method introspection in CFEclipse is something of a fool’s errand.