Updating or Starting Spring 3.0 Project

On December 16, 2009, Spring framework version 3.0 was finally released.

For those with Spring projects already in progress, as well as those starting new projects, a number of new features are available, and some changes must be made to take advantage of them. It is strongly recommended that the full release with documents is downloaded as the changes are sweeping and sometimes drastic. That said, you shouldn’t need to do much differently than before to get the same stuff out of your Spring applications. At the very least, a slew of compiler warnings, if not errors, will be introduced if you just take a “replace the JAR files” approach to upgrading as many of the Controllers and other elements have been deprecated or otherwise reworked.

Downloading the full release with documentation brings forth a new 800-page reference document, which we should all read, but we know not all of us will. This article will endeavor to point out some pitfalls and help make a quick conversion of an existing web project to the new paradigm as well as try to show how to quickly make a new web project from scratch using the new Spring 3.0 framework.

Swing by the Spring download page (http://www.springsource.com/download/community) and grab the release archive, if you haven’t already. Expand it so the files will be at the ready. If you grab the with-docs version (which is recommended at least once), peruse the reference documents for areas you expect to have trouble.

The assumption made is that you’re at least familiar with Spring enough to recognize some of the generalities and translate them to your own project; a word of warning–this isn’t intended to be a “learn Spring” tutorial.

Existing Project Upgrade

This bit assumes you’ve got a Spring 2.x project and you’re looking to upgrade it to the new framework. Presumably if you’ve been developing with the Spring 3.0 beta and release candidates these problems have already been addressed; perhaps not, though, or perhaps there’s some clarity you seek. Hopefully this section helps in either case.

In your project, we’ll assume a standard structure of a source directory for the Java and such, and a web content directory for the web-served bits. In the web content directory lives our magic WEB-INF folder, and in the WEB-INF/lib folder is where the Spring JAR files are located. If your configuration is different, you’ll have to translate, and should probably consider this more concise approach instead.

In a Spring 2.x project, the spring-beans.jar, spring-core.jar, spring-web.jar, and whatever other Spring JAR files you’ve been using will need to be removed to avoid conflicting with the new framework files. Remove also any .tld or .xsd or other files that may have been copied from the old framework folders into the project. A flurry of compiler errors should be evident as we’ve removed core classes required by the project. Don’t worry about correcting any of these just yet as we’ve got to put the replacement JARs in place, which will remove most of those errors, and give us just a few warnings in their place.

As we look in the new Spring 3.0 archive’s dist folder, we see immediately that the file naming format has been changed. For the most part, once you get comfortable with the new format it starts to make sense. In most cases the classes have been packaged inside the JAR that starts with the same package name. For example, the starting point for nearly every Spring web application is the DispatcherServlet. The DispatcherServlet is in the org.springframework.web.servlet package. There should be a org.springframework.web.servlet-3.0.0.RELEASE.jar file in the dist folder. In the Spring 2.x framework, the DispatcherServlet is in the spring-webmvc.jar file. Other classes we may use are likewise tucked into their package-named JAR files. For example, if we use the org.springframework.beans.factory.config.PropertiesFactoryBean, we can find that it is hiding in the org.springframework.beans-3.0.0.RELEASE.jar file.

With this in mind, for each of the missing class errors that our compiler is giving us, copy the appropriate package-named JAR file to our WEB-INF/lib folder. As we recompile, we’ll lose all or most of the errors. I leave the caveat that your project may be doing something different than mine, and that perhaps there’s a dependency that you have that also requires an upgraded JAR…so upgrade those as well. The project I’m basing this on uses Hibernate and some Apache Commons bits, and none of them required any changes to support the upgrade of Spring.

When done, there shouldn’t be any need to change any source files to satisfy any of the errors created by changing the JAR files. In some of the Spring-related XML files, like the applicationContext.xml (or whatever you named yours), there may be a bit that says something like “spring-beans-2.5.xsd” that needs to be changed to “spring-beans-3.0.xsd” or “spring-context” or whatever other bits you’ve used in your application. Change those to match the new version, and that should be it for the change.

It should be the case that the application will again compile and execute as before, without any other changes. Of course, there may be some configuration file quirks, again depending on the complexity of your project, or how hard you banged on Spring to get it to work.

If you’re not already using annotations, though, a diligent eye will notice that we’re now probably left with some compiler warnings. Most notably, any class that extends a Spring Controller will complain that the Controller it’s extending has been deprecated. Spring 3.0 pushes us hard into using annotations.

I was already using annotations for most of my controllers, but I did have one “catch-all” controller for URLs that I hadn’t mapped. The two bits in my Spring configuration that I changed included removing the bean for the AnnotationMethodHandlerAdapter (which in retrospect I may not have needed anyway), and removing the SimpleControllerHandlerAdapter and SimpleUrlHandlerMapping beans I’d configured for my catch-all, replacing that bean instead with @Controller and @RequestMapping(“/*”) annotations which did the same default URL handling.

This reduced the Controller/URL handling parts of my applicationContext.xml to just this bit.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"
		p:viewClass="org.springframework.web.servlet.view.JstlView" />
	<context:component-scan base-package="tld.domain.project.controllers" />
</beans>

Two lines, with wrapping, that controls all of our URL and JSP handling. The first tells the ViewReslover where the related JSP files are, and the second tells Spring in which package I’ve tucked my annotated Controllers. I also have some Hibernate configuration, Locale resolvers, and ResourceBundle bits, but they didn’t change a bit from 2.5 to 3.0, and this is enough to satisfy the annotated Controller discussion. Your paths may vary, and that’s a horrible package name.

If you’re not using annotations, here’s where it gets a little tricky: you’re probably using a specific Controller for specific tasks. Perhaps you’re a fan of the SimpleFormController or you roll your own with the AbstractController. The difficulty in making the change from those to the annotated Controller is going to depend on the complexity of the Controllers used and the functions therein. Let’s take a simple SimpleFormController as an example and convert it to an annotated Controller instead, to give a quick example.

First, one of our application context XML files would contain a bit not unlike this defining our bean and telling Spring to handle all otherwise unmapped URLs with our controller.

	<bean id="exampleSimpleFormController" 
		class="tld.domain.project.ExampleSimpleFormController">
		<property name="commandClass" value="tld.domain.project.CommandClass" />
		<property name="commandName" value="Command" />
		<property name="formView" value="index" />
		<property name="successView" value="index" />
	</bean>
	<bean
		class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
	<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
		<property name="order" value="99" />
		<property name="mappings">
			<props>
				<prop key="/*">exampleSimpleFormController</prop>
			</props>
		</property>
	</bean>

Our Controller class would look something like this, presumably doing a little bit more work between the call and return, but this actually meets our criteria for the example.

public class ExampleSimpleFormController extends SimpleFormController {
    @Override
    protected ModelAndView handleRequestInternal(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse) throws Exception {
	// Do your work here
        return super.handleRequestInternal(httpServletRequest, httpServletResponse);
    }
}

We can convert this to an annotated Controller without too many changes and losing no functionality. Simply remove those bits in the application context XML file related to this bean, make sure it’s in a package covered by the annotated class scan (the context:component-scan bit above). Make the following changes, and you’ve converted the controller to an annotated one.

@Controller
public class ExampleSimpleFormController {
    @RequestMapping("/*")
    public String handleRequestInternal(final HttpServletRequest httpServletRequest, 
		final HttpServletResponse httpServletResponse) throws Exception {
	// Do your work here
        return “index”;
    }
}

Again, of course, the complexity of your Controller will make that more difficult. This class, however, will function exactly the same in both versions. The name of the class can be changed, as can the name of the method handling our default request, with consideration to use elsewhere.

One obnoxious thing about this example is that it’s tediously simple. All it’s really going to do is handle any URL request not matched by another Controller and return the rendered WEB-INF/index.jsp page. The original Spring configuration implied, as a SimpleFormController would, that some action would be taking place, as we had defined a command object and had to define both success and failure view names. Let’s make a tougher example and convert it from a SimpleFormController to an annotated Controller with some simple form input.

Let’s begin with a JSP fragment that contains this simple login form:

<div id="form">
<form:form action="login.ext" commandName="Login" method="POST">
<div><label for="user">Name</label> <form:input path="user" /></div>
<div><label for="password">Password</label> <form:input path="password" /></div>
<div><input id="submit" type="submit" value="Log-in" /></div>
</form:form>
</div>

Note that the .ext used in the action should match whatever URL mapping done in the web.xml tying the URL to the DispatcherServlet. It could be that /* is handled by the DispatcherServlet, but that’s not recommended. We’d back this with an Object that presumably had a pair of member Strings, for the name and password, clumsily shortened for brevity like this:

public class Login {
    public String user = null;
    public String password = null;
}

We’d reference this class as our Controller’s command object, giving it the same name as the one in our form, all tied into our bean definition in an appropriate XML file. The Controller class then probably would have getters and setters to set the object. We can eliminate most of that busy work with our new annotated Controller that does a little trivial validation.

@Controller
@RequestMapping("/login")
public class LoginController {

    @RequestMapping(method = RequestMethod.GET)
    public String handleGET() {
        return “login”;
    }

    @RequestMapping(method = RequestMethod.POST)
    public String handlePOST(@ModelAttribute("Login") final Login login) {
        if ((login != null) && (login.user != null) && (login.password != null)) {
	      return “index”;
        }
       return “login”;
    }
}

Note that while the .ext was specified in the form, we can leave it out (or put it in) in the RequestMapping, which will by default then map all login.* and login/ requests to this Controller unless a better match to the URL is found. When the Controller gets a GET request, it simply renders the login.jsp page. When it gets a POST request, it verifies that the user and password fields have values (I did say trivial), and then renders the index.jsp page, otherwise it re-renders the login.jsp page.

A more comprehensive example will follow as we set up a new simple web application in the next section, but that’s the crux of a quick transformation from Spring 2.x to Spring 3.0 that will work with most projects. A little work to transform deprecated Controllers to annotated Controllers, but that can be postponed for a bit and done as time allows.

New Project Basics

Sadly, in my opinion, the Spring documentation spreads out all of the bits and pieces necessary to make a project from nothing. Additionally, what strong suggestions there are tend to start with “use the sample project and remove what you don’t need,” which is both tedious and intimidating if you’re not sure what is safe to remove. I prefer an approach of starting with nothing and add the minimum necessary to get things going.

Of course, a bit of up front design is always nice; it’s hard to make a project without any kind of intention behind it. For the purposes of this example, we’ll make a trivial in-memory Twitter clone out of just a couple of annotated Spring Controllers and the JSPs that render the pages. No security, graphics, or style sheets to clog the works, just simple annotations and tags.

For the view, we’ll have a simple one-page interface that will give us a simple form to add a post, a simple search form, and a list of the posts thus far, ordered in descending date (newest on top).

For our controllers, we’ll need something to handle the search, something to handle the post, and something to give us the list of previous posts. We’ll use a simple multi-action controller with a different target for each of our form actions.

For our model, we’ll simply have a post object, and we’ll maintain a small collection of them (so we don’t overwhelm our example system or run out of memory.

We’ll assume and discuss as if the whole world uses Java6, Tomcat v6, and Eclipse, and that all of the paths are correctly configured, and that Eclipse has a Tomcat server configured. In Eclipse, make a new Dynamic Web Project; give it a name (I’ll call it “microblog”), associate it with the Tomcat server, let it create source and web content folders, and generate a default web.xml file.

Since we know we’re going to use Spring for our framework, let’s put that in our application. Start with editing the WEB-INF/web.inf file in the web content folder. By default the XML contains a description that has the display-name of the application and a generous list of welcome-files. Reduce the welcome-file-list to just one, like index.htm and create an empty file in the root of the web content folder of the same name; a quirk in Tomcat will throw a 404 error if the file doesn’t exist, even though it won’t be used as we’ll replace it with a Spring Controller in just a bit.

A quick fix to another little Tomcat quirk, add a context-param for the webAppRootKey to avoid collisions with other applications. Give it a unique name so that Tomcat won’t complain every time the app starts, nor will any application fail because suddenly it’s looking in the wrong folder. The param-name needs to be webAppRootKey, but the param-value should be unique within your Tomcat server. Put this between the display-name and welcome-file-list.

	<context-param>
		<param-name>webAppRootKey</param-name>
		<param-value>microblog.appRoot</param-value>
	</context-param>

Then since we’re going to be making a really simple Spring application, we’ll start with the textbook minimum configuration. We need to declare our Servlet to handle our requests, and map URLs to the servlet. Put this between the context-param (or description if you and welcome file list

	<servlet>
		<servlet-name>microblog</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/applicationContext.xml</param-value>
		</init-param>
	</servlet>

	<servlet-mapping>
		<servlet-name>microblog</servlet-name>
		<url-pattern>*.htm</url-pattern>
	</servlet-mapping>

The servlet definition tells Tomcat that there’s a servlet named “microblog” of the type DispatcherServlet, with the provided parameter. The parameter will be passed to the DispatcherServlet and tell it where to find the context information; we’re going to tuck it away in the WEB-INF folder where web browsers can’t get to it. The servlet-mapping definition tells Tomcat that all URLs reaching this application that end in .htm will be handled by our microblog Servlet.

This gives us a simple WEB-INF/web.xml file that looks like this one.

<?xml version="1.0" encoding="UTF-8"?>
<web-app
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	id="microblog_ID" version="2.5">
	<display-name>microblog</display-name>
	<context-param>
		<param-name>webAppRootKey</param-name>
		<param-value>microblog.appRoot</param-value>
	</context-param>
	<servlet>
		<servlet-name>microblog</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/applicationContext.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>microblog</servlet-name>
		<url-pattern>*.htm</url-pattern>
	</servlet-mapping>
	<welcome-file-list>
		<welcome-file>index.htm</welcome-file>
	</welcome-file-list>
</web-app>

Since we’ve declared that we’re going to use the DispatcherServlet, we’ll need to copy the appropriate Spring JAR file to our WEB-INF/lib folder. DispatcherServlet is in the org.springframework.web.servlet package, which most closely matches the org.springframework.web.servlet-3.0.0.RELEASE.jar, so copy that one. You can verify by searching for the type and Eclipse should be able to find it.

Although we’re going to be annotating our Controllers and such, some may point out how we’re sticking with the old XML way of configuring Spring. This is due in part to the comfort we have with the XML configurations, the trivial configuration we’re going to have, and finally because the Spring documentation that tells us that annotating the configuration is not a 100% replacement. Rather than anything confusing at this point, we’ll stick with the trivial XML we need to make our application.

<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
	<bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"
		p:viewClass="org.springframework.web.servlet.view.JstlView" />
	<context:component-scan base-package="com.objectpartners.microblog" />
</beans>

This was discussed before, but so you don’t have to scroll back, this essentially two-line XML file (one bean, one context element) tells our DispatchServlet what we’re going to use for our view resolver (to render our JSPs), and to scan our package for annotations. Since we’ve stated that our JSPs are going to be in WEB-INF/jsp, we should take this opportunity to create that folder. Additionally, we’ve declared our package to scan, so we should create that path in our folder, too; in Eclipse we just create an empty package. Our two additional classes are in the org.springframework.web.servlet package with our DispatcherServlet, so we don’t need to copy an additional JAR file just yet.

Where to go next is a choice of style and discipline; controller, model, or view, or all at once. I think we need somewhere for the first URL request to go, so let’s build a simple catch-all controller to deliver our default page, and see where that leads us.

Our project is going to be terribly small, so despite any other design patterns, we’re going to put everything right in our previously created package. Let’s create a class named CatchAll to catch everything that isn’t otherwise handled.

package com.objectpartners.microblog;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class CatchAll {
	@RequestMapping("/*")
	public String catchAll() {
		return "index";
	}
}

Curious, the org.springframework.stereotype.Controller isn’t found in a JAR named after the package, and it doesn’t give any hint that this is an annotation class, either. We can find this is the right fully-qualified class name in the Spring API documentation, but finding it in the JAR files is a bit tedious. It’s hiding in the org.springframework.context-3.0.0.RELEASE.jar which is one of the few deviations from the new naming scheme we’ll run into. The RequestMapping annotation is correctly in the org.springframework.web-3.0.0.RELEASE.jar file. Add both of those files to our WEB-INF/lib and any compile errors so far will be resolved.

Our handler is woefully simple, and will tell Spring to render the index.jsp for every URL received. We told Spring before that these files would exist in the WEB-INF/jsp folder, so let’s make one right now; we’ll revisit and make it useful in a moment. Simply add a “hello, world” JSP so we can test that everything works so far; using Eclipse, just add a new JSP page to the folder, name it index.jsp, and add some text between the body tag.

So far, all looks well, and while not meeting our project goals, we should be at an acceptable starting point. That is, the app looks like it completes all of the required configuration elements. Add the project to the configured Tomcat server in Eclipse and start it up. Tomcat should start with no problem, but if we try to visit it (http://localhost:8080/microblog/), we’ll end up with an exception because we’re missing some classes we didn’t declare, but that Spring needs to function. There are going to be a lot of missing classes, so be prepared. In our Tomcat console, and probably the web page, we should see a stack trace that warns us that there’s a missing class.

java.lang.NoClassDefFoundError: org/springframework/beans/BeansException

We need to add org.springframework.beans.BeansException, which is in the org.springframework.beans-3.0.0.RELEASE.jar file, so copy that to the WEB-INF/lib folder and restart Tomcat. A second try, another exception for another missing class.

java.lang.NoClassDefFoundError: org/springframework/core/NestedRuntimeException

Copy the org.springframework.core-3.0.0.RELEASE.jar file to the WEB-INF/lib folder, restart Tomcat, and try again. We could have almost guessed we’d need beans and core, but we’re trying to add as few files as possible, remember. Trying again, and, yes, another failure. This time from a dependency Spring has for logging.

java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory

Quickly scanning the documentation, we can see that this is left to us to decide for ourselves which implementation we can or may want to use. Some application servers provide such logging, some environments need a little extra configuration. We could rebuild Spring without the dependency, or the easy solution, get the latest JAR from Apache Commons Logging (http://commons.apache.org/logging/) and add it to our WEB-INF/lib folder. Restart, retry, re-fail.

java.lang.NoClassDefFoundError: org/springframework/asm/ClassVisitor

Copy org.springframework.asm-3.0.0.RELEASE.jar to WEB-INF/lib, restart, reload.

java.lang.NoClassDefFoundError: org/springframework/expression/PropertyAccessor

This one is hiding in org.springframework.expression-3.0.0.RELEASE.jar so copy that, restart, reload.

The next one gets a little tricky.

java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config

While this should be a part of Tomcat, it isn’t. I suppose it’s possible that the tag library might not be used by enough Servlet or JSP applications to make it a default. If you’re not using Tomcat, you might have this in your J2EE engine’s classpath already. Since we are running Tomcat in our example, grab the Taglibs from the Apache website (http://tomcat.apache.org/taglibs/standard/). Grab the version 1.1 file (1.2 isn’t quite done yet) and copy the JAR files from the downloaded lib folder to your WEB-INF/lib folder. Restart Tomcat.

Finally, we should be rewarded with the text of our index.jsp page. Our annotated Spring web application works for now. Back to making it useful.

Since we were last editing the index.jsp, let’s add the tags and form elements to display a list of our micro-blogging posts and some simple forms for submitting a post and searching.

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%><%@ taglib prefix="form"
	uri="http://www.springframework.org/tags/form"%><%@ taglib prefix="c"
	uri="http://java.sun.com/jsp/jstl/core"%><!DOCTYPE html 
	PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
	"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Microblog</title>
</head>
<body>
<div><form:form action="post.htm"
	commandName="Post" method="POST">
	<div><form:textarea path="post" cols="60" rows="3" /></div>
	<div><input id="submit" type="submit" value="Post!" /></div>
</form:form></div>

<div><form:form action="search.htm"
	commandName="Search" method="POST">
	<div><form:input path="post" maxlength="60" size="40" /></div>
	<div><input id="submit" type="submit" value="Search!" /></div>
</form:form></div>

<div><c:forEach var="post" items="${Posts}">
	<div>
	<div>${post.post}</div>
	<div>${post.time}</div>
	</div>
</c:forEach></div>
</body>
</html>

Near the top we added a couple taglib lines telling the JSP renderer to use the Spring form and JSTL core tags. The first form is a big text area with a button to submit; it’s aiming for the post.htm action, and will use a page bean named “Post” for storing its data. The second form is an input and a submit button; it’s aiming for the search.htm action and will use a page bean named “Search” for storing its data. Finally, there’s a forEach loop that will simply spew whatever comes out of the array or collection page bean named “Posts.”

Let’s throw together a quick bean to satisfy the forms. We’ll cheat and use the same little guy, for brevity. A simple bean with two Strings.

package com.objectpartners.microblog;

public class Post {
	private String post = null;

	private String time = null;

	public String getPost() {
		return post;
	}

	public String getTime() {
		return time;
	}

	public void setPost(String post) {
		this.post = post;
	}

	public void setTime(String time) {
		this.time = time;
	}
}

We’ve already got our CatchAll Controller, so let’s just tweak that to handle these new requests.

package com.objectpartners.microblog;

import java.util.LinkedList;
import java.util.Calendar;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class CatchAll {

	@RequestMapping("/*")
	public String catchAll(final HttpServletRequest httpServletRequest,
			final ModelMap modelMap) {
		modelMap.addAttribute("Post", new Post());
		modelMap.addAttribute("Search", new Post());

		final Object object = httpServletRequest.getSession()
				.getServletContext().getAttribute("Posts");
		if (object instanceof List<?>) {
			modelMap.addAttribute("Posts", object);
		}

		return "index";
	}

	@RequestMapping("/post")
	@SuppressWarnings("unchecked")
	public String post(final HttpServletRequest httpServletRequest,
			@ModelAttribute("Post") final Post post, final ModelMap modelMap) {

		LinkedList<Post> posts = null;
		final Object object = httpServletRequest.getSession()
				.getServletContext().getAttribute("Posts");
		if (object instanceof LinkedList<?>) {
			posts = (LinkedList<Post>) object;
		} else {
			posts = new LinkedList<Post>();
			httpServletRequest.getSession().getServletContext().setAttribute(
					"Posts", posts);
		}
		if (post.getPost() != null && !post.getPost().trim().isEmpty()) {
			post.setTime(Calendar.getInstance().getTime().toString());
			posts.offerFirst(post);
		}
		while (posts.size() > 100) {
			posts.pollLast();
		}
		modelMap.addAttribute("Posts", posts);

		modelMap.addAttribute("Post", new Post());
		modelMap.addAttribute("Search", new Post());

		return "index";
	}

	@RequestMapping("/search")
	@SuppressWarnings("unchecked")
	public String search(final HttpServletRequest httpServletRequest,
			@ModelAttribute("Search") final Post search, final ModelMap modelMap) {

		final Object object = httpServletRequest.getSession()
				.getServletContext().getAttribute("Posts");
		if (object instanceof List<?>) {
			if (search.getPost().trim().isEmpty()) {
				modelMap.addAttribute("Posts", (List<Post>) object);
			} else {
				final List<Post> posts = new LinkedList<Post>();
				for (final Post post : (List<Post>) object) {
					if (post.getPost().toLowerCase().contains(
							search.getPost().toLowerCase())) {
						posts.add(post);
					}
				}
				modelMap.addAttribute("Posts", posts);
			}
		}

		modelMap.addAttribute("Post", new Post());
		modelMap.addAttribute("Search", new Post());

		return "index";
	}
}

We’ve upgraded our default request method, catchAll(), adding a couple parameters that Spring will autowire for us. We need the HttpServletRequest to gain access to the collection we’re maintaining in memory, or more specifically in the Servlet context’s memory (we could use a static variable). We need the ModelMap to store our page beans for the form. We could annotate these a little better, getting directly to the individual beans, but that actually adds as much extra class material to build what will ultimately result in a bunch of empty objects. The catchAll() simply grabs the collection from the ServletContext, if it exists, and sends back that collection and some empty beans to satisfy the JSP.

We added a post() method, and mapped that to the /post request to handle when the user taps the “Post!” button from the form. Note the ModelAttribute annotation telling Spring to associate the form data for a form named “Post” with the parameter. This method will create the collection if it doesn’t already exist. It validates that the post contains text (and nothing more) and adds it to the head of the list. It also truncates the list to be sure our simple list won’t get too long. Like catchAll(), it puts the necessary elements in the ModelMap to render the form.

Finally, the search() method will handle the /search request when the user taps the “Search!” button. Like the post() method it has an annotation allowing Spring to autowire the form data. This method searches for all posts containing the string as input. If the string is empty, all post elements are returned. The ModelMap is populated and the form is rendered.

In our trivial application, we have (after much trial-and-error correction) the minimum Spring and other JAR files necessary to support our application. We’ve got one annotated Controller class, handling all of the actions our one JSP page can dish out. It isn’t pretty, but it works.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*