Spring MVC with ColdFusion Views
Ok CF developers, remember the matrix, is it time to take the red-pill yet? There are a lot of frameworks in ColdFusion, and if you like them great. However if you need too, or want too, mix CF and Java wouldn't it be cool if you could use a leading Java framework with ColdFusion instead?
First, a little background. Recently I tried JSP for a side project, to see if things have changed - they haven't. JSP pages are just as painful as they always were. There is just something right about mixing one tag based language (HTML) with another tag based language (ColdFusion). However at the same time I've been using Spring with BlazeDS to build a lot of services for flex applications. And there is something that feels right about using java for the back-end. So that got me thinking..
What I want is CFML with Java code-behind. Just like how you can have MXML with ActionScript code-behind, or XAML and C#. But how? After some digging and a bit of coding what I found was a way to do it with the Spring MVC project.
Spring MVC has some very powerful features, but this isn't a 'what is SpringMVC' article - so I urge you to read up on what it can do. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-introduction
However one feature I do want to talk about is the way Spring MVC forces a hard separation between the controller and the view layers allowing for multiple views driven from the same model and controller. And that is how we are going to drive CF from Spring.
For example: Let's say you had a mapping defined as /userList.*
If you call "/userList.cfm" you will get a cfml page. But what if you want to call the same page from your mobile application and need to get back a pure JSON version of the same data used to render the html. You could use this url "/userList.json". With Spring MVC the exact same model and contoller code will be run for both requests - only the view is different. With the *.cfm mapping Spring MVC sends the request to the ColdFusion servlet. Using the *.json mapping Spring can send the request to a custom view class that will format the model arguments as JSON and return the results. Technically you can configure it so , *.cfm, *.html, *asp, *.jsp extensions all go to the same ColdFusion Servlet too (if you like to mess with your fellow developers).
Or perhaps you want different variations of html for different platforms.
- /index.web
- /index.mobile
These can all be mapped to completely different cfm templates with different code for the different platforms (just a thought).
Ok time to talk code. How to use ColdFusion as the view-layer in a Spring MVC application.
1. Create a java controller class. with the @Controller annotation
@Controller
public class LitepostController
{
}
2. Add a method fo handle requests. WIth the @RequestMapping annotation.
@RequestMapping(value = "/index.*")
public ModelAndView indexHandler(HttpServletRequest request, HttpServletResponse response) throws Exception
{
}
3. In the method, create and return a ModelAndView object. This string you define in the constructor will be the cfml template you really want to be invoked.
@RequestMapping(value = "/index.*")
public ModelAndView indexHandler(HttpServletRequest request, HttpServletResponse response) throws Exception
{
if (request.getSession().getAttribute("user") != null)
{
// Start the app by directing the use to a login page.
ModelAndView view = new ModelAndView("login"); //invoke the login.cfm template
return view;
}
ModelAndView view = new ModelAndView("index"); //invoke the index.cfm template
view.addObject("user", request.getSession().getAttribute("user") ); // this property will be accessible in the #request.USER# variable
return view;
}
4. Define a new Servlet in the web.xml file. Directing all /myApp/* requests to the controller above.
<servlet>
<servlet-name>litepost-mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Map all REST request to /requestbroker -->
<servlet-mapping>
<servlet-name>litepost-mvc</servlet-name>
<url-pattern>/myApp/*</url-pattern>
</servlet-mapping>
5. Spring will look for an XML config file using the pattern "
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"></bean>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"></bean>
<bean id="litepostService" class="com.mikenimer.cfspringmvc.litepost.controllers.LitepostController">
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1"/>
<property name="prefix" value="/litepost/cfspringmvc-inf/views/"/>
<property name="suffix" value=".cfm"/>
<property name="viewClass" value="com.mikenimer.spring.mvc.views.ColdFusionView"/>
</bean>
</beans>
The trick in this file is the ViewResolver, which has 4 properties.
- order - the order it should try the
- suffix - the extension for this view, when there are more then one view resolver to pick from
- prefix - the real location of the cfml templates you want executed.
- viewClass - This is a custom view resolver I created (which you can download below). This custom class knows how to take the java model properties, returned from the controller, and put them into the ColdFusion REQUEST scope.
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1"/>
<property name="suffix" value=".cfm"/>
<property name="prefix" value="/litepost/cfspringmvc-inf/views/"/>
<property name="viewClass" value="com.mikenimer.spring.mvc.views.ColdFusionView"/>
</bean>
That's it.. Now Spring MVC controller class will process requests for "/myApp/index.cfm" first, then execute the correct CFML template. And any properties you return in the ModelAndView object will be put automatically into the ColdFusion REQUEST Scope.
And some extra java utils to go with it, yes! In building the sample litepost application I found that I needed two simple methods to get things working right. So I create a CFJAVAUTILS object with some extra helper methods. This object will also be placed into the request scope of every request.
- ifNull( Object object, Object default )
This method will take any java object an if it's null, return the default instead. For instance #request.CFJAVAUTILS.ifNull( user.getName(), "")# - toCFArray( Collection collection )
A lot of java objects support the Collection interface. However, ColdFusion arrays functions and loop functions have problems with them so I've created a simple collection to vector (also known as an Array in CF). for example: #arrayLen( request.CFJAVAUTILS.toCFArray( entry.getComments() ) )#
You want to see it in action? Ok, there is a sample application out there, called Litepost ( http://code.google.com/p/litepost/ ), which is used to show the same application coded with different frameworks. So I've used this application as the sample app, so you can see a real-world application using Spring MVC, compared with other techniques.
After you download and install the litepost application. Download my cf-spring litepost application below. To install, unzip the file and put the cfspringmvc-inf folder under the /litepost directory. Really it doesn't matter where you put it, as long as you modify the mapping in the spring config to match (and it's in a folder CF has permission to serve files from)
I've included the a list of jars required for this sample application to work in the WEB-INF-Config folder. I've also made this example java heavy - doing all of the data work with hibernate in the java layer. That is to appeal to the more java focused of you out there. But you could use this just to control the flow of templates or take it as far as I did doing all of the data work in the java layer.
Some other areas I'm planning on exploring in future posts
- Multiple View-Resolvers
- Integration with Spring Security
- Integration with the Spring Integration Bus
- Spring Web-Flow with CF
- File Uploading
- Sending emails
- Using the Spring JSP tags in a CFML template (I think this will work).
If anyone wants to write up a how-to on one of these, or any other examples, let me know and I'll link to them here. Plus, I'm sure some of you know spring a lot better then I do, so to get some more views on this would be great for everyone.
Downloads:

