16. Integrating with other web frameworks – Spring in Action, Second Edition

Chapter 16. Integrating with other web frameworks

This chapter covers

  • Using Spring with Jakarta Struts

  • Integrating with WebWork 2 and Struts 2

  • Working with Tapestry

  • Using JavaServer Faces with Spring

  • Ajax-enabling with DWR

Do you always order the same thing when you go out to eat? If you’re like a lot of people, you have your favorite dish that you enjoy and rarely, if ever, try anything new. You’ve heard good things about the pasta frijole, but you know that the lasagna’s good. So, instead of trying something different, you stick with what you’re comfortable with.

Likewise, perhaps you’re already quite comfortable with a specific web framework. You have heard good things about Spring MVC, but you’re not quite ready to take the leap. You may have legitimate reasons for choosing a different web framework to front your application. Perhaps you’re already heavily invested in another MVC framework such as Struts or WebWork and aren’t prepared to abandon it for Spring. Nevertheless, you would still like to take advantage of Spring’s support for dependency injection, declarative transactions, AOP, and so forth.

No problem. As we’ve seen throughout this book, the key to the Spring Framework is the freedom to choose what works best for your application. As you’ll learn in this chapter, Spring offers you choices when building web applications, too. Although Spring offers its own very capable web framework, you are free to choose another if you’d like and still be able to take advantage of Spring in the other layers of your application.

If you’re not quite ready to make the jump to Spring MVC, you certainly have a huge selection of other web frameworks to choose from. In fact, there are hundreds of web frameworks for Java. I have neither the space nor the inclination to show you how to integrate Spring with all of them. But we’ll show you how Spring can be used with a few of the more popular MVC frameworks, including Struts, WebWork, Tapestry, and JavaServer Faces (JSF). We’ll also see how to use Spring with a popular Ajax toolkit known as DWR (Direct Web Remoting).

Struts has long been the workhorse framework of many Java-based web applications. As it’s the most well known among all of the web frameworks that Spring integrates with, let’s start this chapter by seeing how to use Spring and Struts together.

Using Spring with Struts

Despite the seemingly endless barrage of Java-based MVC frameworks, Struts is still the king of them all. It began life in May 2000 when Craig McClanahan launched the project to create a standard MVC framework for the Java community. In July 2001, Struts 1.0 was released and set the stage for Java web development for thousands and thousands of projects.

In this section, we’re going to look at how to use Struts in the web layer of a Spring-enabled application. While we will cover just enough Struts basics to accommodate integration with Spring, we won’t be going into any great detail on how to develop applications using Struts. If you want more information on Struts, I recommend you check out Struts in Action (Manning, 2002) and Struts Recipes (Manning, 2004).

Let’s turn back the clock and pretend that we had developed the RoadRantz application using Struts instead of Spring MVC. Had that been the case, we would’ve had written RantsForVehicleAction (listing 16.1) instead of RantsForVehicleController (see listing 13.3).

Example 16.1. A Struts-flavored look at the RoadRantz application

This Struts action performs the same functionality as the RantsForVehicle-Controller that we wrote in chapter 13. Just like that Spring MVC controller, RantsForVehicleAction uses a RantService to retrieve a list of rants for a particular vehicle. It then places the list into the request so that the view layer can render the list in the browser.

But listing 16.1 isn’t complete. What’s missing is the part that tells where the RantService comes from.

In the Spring MVC version, RantsForVehicleController is configured in the Spring application context just like any other Spring-managed bean. As a Springmanaged bean, RantsForVehicleController could be given a RantService through dependency injection.

RantsForVehicleAction, on the other hand, is a Struts-managed action class. This means that Spring won’t normally have any idea that it exists, much less that it needs to be injected with a RantService.

It would seem that we have a problem. If RantsForVehicleAction isn’t a Spring-managed bean then how can we give it a RantService that is managed by Spring?

Fortunately, Spring offers a solution for Struts-integration. Actually, Spring offers two solutions to choose from:

  • Write Struts actions to extend a Spring-aware base class.

  • Delegate requests to Struts actions configured in the Spring application context.

We’ll explore each of these strategies for Struts-Spring integration in the sections that follow. But first, regardless of which approach you take, there’s one bit of common configuration that you’ll need to take care of: telling Struts about your Spring application context.

Registering the Spring plug-in with Struts

In order for Struts to have access to Spring-managed beans, you’ll need to register a Struts plug-in that is aware of the Spring application context. Add the following code to your struts-config.xml to register the plug-in:

<plug-in className=
    "org.springframework.web.struts.ContextLoaderPlugIn">
  <set-property property="contextConfigLocation"
       value="/WEB-INF/classes/roadrantz-servlet.xml,
            /WEB-INF/classes/roadrantz-services.xml,
            /WEB-INF/classes/roadrantz-data.xml,
            /WEB-INF/classes/roadrantz-data-hibernate.xml,
            /WEB-INF/classes/roadrantz-cache.xml"/>
</plug-in>

ContextLoaderPlugIn loads a Spring application context (a WebApplicationContext, to be specific), using the context configuration files listed (comma-separated) in its contextConfigLocation property.

Now that the plug-in is in place, you’re ready to choose an integration strategy. Let’s first look at how to create Struts actions that are aware of the Spring application context.

Writing Spring-aware Struts actions

One way to integrate Struts and Spring is to write all of your Struts action classes to be aware of the Spring application context. Spring’s WebApplicationContextUtils class provides some convenient static methods that can be used to retrieve the application context. With the context in hand, you can then use Spring as a factory to retrieve the beans your Struts actions needs. For example, here’s how the execute() method of RantsForVehicleAction could be written:

public ActionForward execute(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws Exception {

  VehicleForm vehicleForm = (VehicleForm) form;
  Vehicle vehicle = vehicleForm.getVehicle();

  ApplicationContext ctx =
      WebApplicationContextUtils.getRequiredWebApplicationContext(
      getServlet().getServletContext());
  RantService rantService =
      (RantService) ctx.getBean("rantService");

  request.setAttribute("rants",
      rantService.getRantsForVehicle(vehicle));

  return mapping.findForward("rantList");
}

The code in bold uses the getRequiredWebApplicationContext() method of WebApplicationContextUtils to retrieve the Spring application context and ultimately to retrieve the rantService bean.

Since a typical Struts application will involve more than one action class, you could end up repeating that same context lookup code in all of them. To avoid duplicating code, it’s better to put the Spring context code in a common base class to avoid code duplication. Then each of your application’s action classes would subclass the base action class instead of Struts’s Action.

The good news is that you won’t have to write the Spring-aware base action class. That’s because Spring comes with ActionSupport, an extension of Struts’s Action that overrides the setServlet() method to retrieve the Spring application context from the ContextLoaderPlugIn. Any class that extends ActionSupport has access to the Spring application context by calling getWebApplicationContext(). From there, the action class can retrieve beans directly from Spring by calling the getBean() method, as shown in figure 16.1.

Figure 16.1. Spring’s ActionSupport is a convenient base class for Action implementations that makes the Spring application context directly available to the action class. From there the action can retrieve its dependencies from the Spring application context.

For example, consider RantsForVehicleAction in listing 16.2, which extends ActionSupport for access to the Spring application context.

Example 16.2. A Spring-aware implementation of RantsForVehicleAction

When RantsForDayAction needs a RantService, it starts by calling getWebApplicationContext() to retrieve the Spring application context. From there it simply calls getBean() to retrieve the rantService bean.

The good thing about using this approach to Struts-Spring integration is that it’s very intuitive. Aside from extending ActionSupport and retrieving beans from the application context, you are able to write and configure your Struts actions in much the same way as you would in a non-Spring Struts application.

But this approach also has its negative side. Most notably, your action classes will directly use Spring-specific classes. This tightly couples your Struts action code with Spring, which may not be desirable. Also, the action class is responsible for looking up references to Spring-managed beans. This is in direct opposition to the notion of dependency injection (DI).

For those reasons, Spring offers another way to integrate Struts and Spring. The other approach lets you write Struts action classes that are completely unaware they are integrated with Spring. And you can use Spring’s dependency injection to inject service beans into your actions so that they don’t have to look them up for themselves.

Delegating to Spring-configured actions

As you may recall from chapter 8, Acegi security employs several servlet filters to secure web applications. But so that Spring can inject dependencies into those filters, Acegi provides FilterToBeanProxy, a servlet filter that doesn’t perform any real functionality itself but instead delegates to a Spring-configured filter bean.

As it turns out, the delegation approach used in Acegi can be applied equally well when integrating Struts and Spring. To accommodate Struts action delegation, Spring comes with DelegatingRequestProcessor, a replacement for Struts’s default RequestProcessor that looks up Struts actions from the Spring application context.

The first step in Struts-to-Spring delegation is to tell Struts that we want to use DelegatingRequestProcessor instead of its normal request processor. To do this, we add the following XML to the struts-config.xml:

<controller processorClass=
    "org.springframework.web.struts.DelegatingRequestProcessor"/>

Optionally, if you are using Tiles in your Struts applications, you’ll want to use DelegatingTilesRequestProcessor instead:

<controller processorClass=
    "org.springframework.web.struts.
      DelegatingTilesRequestProcessor"/>

DelegatingRequestProcessor (or its Tiles-savvy cohort, DelegatingTilesRequestProcessor) tells Struts to automatically send action request to Struts actions that are configured in the Spring application context, as shown in figure 16.2. The way it finds the Spring-configured action depends on how you configure the action in struts-config.xml.

Figure 16.2. DelegatingRequestProcessor sends requests from Struts’s ActionServlet to a Struts Action class configured in the Spring application context.

In its simplest form, you can configure an <action> element in struts-config.xml like this:

<action path="/rantsForVehicle" />

When a request comes in for /rantsForVehicle.do, DelegatingRequestProcessor will automatically refer to the Spring application context, looking for a bean named /rantsForVehicle—the same as the action’s path. That means we’ll need to wire the action as a bean in Spring. Let’s do that now.

Wiring actions in Spring

Here’s where the real benefit of using DelegatingRequestProcessor comes in. Rather than configure the RantsForVehicleAction in struts-config.xml (where it is outside of Spring’s jurisdiction and can’t be injected), we will configure it in the Spring application context (roadrantz-struts.xml) as follows:

<bean name="/rantsForVehicle"
    class="com.roadrantz.struts.RantsForVehicleAction">
  <property name="rantService" ref="rantService" />
</bean>

As you can see, RantsForVehicleAction is configured in Spring just like any other bean. And, since it needs a RantService to do its job, we’ve obliged by wiring it with a reference to the rantService bean.

Take note of how the bean is named. Because DelegatingRequestProcessor will look for a bean with the same name as the action’s path and because the path contains a slash character, we had to use the name attribute to name the bean. According to XML rules, the slash character is not allowed in an XML element’s id attribute.

If it seems odd to have the Spring bean named with the same name as the Struts action’s path (or if that slash vexes you) then you may want to configure the <action> element in struts-config.xml like this:

   <action path="/rantsForVehicle"
       type="com.roadrantz.struts.RantsForVehicleAction" />

This <action> declaration looks more like conventional Struts in that the action’s type is declared in the Struts configuration. The big difference is that instead of Struts taking responsibility for instantiating and managing RantsForVehicleAction, DelegatingRequestProcessor will rely on Spring to manage the action as a bean. When a request comes in for /rantsForVehicle, DelegatingRequestProcessor will ask the Spring application context for a bean whose type is com.roadrantz.struts.RantsForVehicleAction and send the request to that bean.

Now we’ve wired the action in Spring and configured it in struts-config.xml. The only thing left to do is to tweak the RantsForVehicleAction class so that it may receive the injected RantService bean, instead of having to look it up itself.

Implementing the Spring-configured Struts action

The dependency-injected version of RantsForVehicleAction isn’t much different from what we showed you in listing 16.1. In listing 16.2 RantsForVehicleAction had to directly use the application context to retrieve the RantService bean. But as you can see in listing 16.3, that’s no longer necessary.

Example 16.3. A Spring-injected RantsForVehicleAction

Because RantsForVehicleAction is configured in Spring, it is given a RantService through the setRantService() method.

What about Struts 2?

Struts is dead. Long live Struts 2!

If you’re a fan of Struts, you’re probably aware that there’s a change coming to the Struts framework. Struts 2 (or Struts Action Framework, as it’s sometimes referred to) is a completely different approach to building MVC applications from the Struts 1.x programming model. As I’m writing this, Struts 2 has recently released its first general availability release, marking it as a production-ready web framework.

What’s interesting about Struts 2 is that it’s mostly a rebranding of another popular web framework. According to the Struts 2 website (http://struts.apache.org/2.x/): “Apache Struts 2 was originally known as WebWork 2. After working independently for several years, the WebWork and Struts communities joined forces to create Struts 2. This new version of Struts is simpler to use and much closer to how Struts was always meant to be.”

Identity crises notwithstanding, how can we integrate Spring with Struts 2? Well, since Struts 2 is effectively the same as WebWork 2, we couldn’t ask for a better segue into the next section. Coming up next, we’ll look at how to integrate Spring with WebWork 2 (and Struts 2).

Working Spring into WebWork 2/Struts 2

WebWork is an open source web framework from Open Symphony that has been popular for quite some time. Despite its name, WebWork is actually a service invocation framework that is not specific to just web applications. In its simplest form, WebWork is based around general-purpose actions. These actions process requests and then return a String that indicates the next step in the request chain. This could be another action or a view. However, nothing about this is web specific.

Nevertheless, for our purposes we will be discussing WebWork in the context of web applications. We’re going to assume that you are already familiar with WebWork and that you’re reading this section to see how you can use Spring with your WebWork applications. Therefore, we’ll only cover just enough WebWork to accommodate the integration with Spring. For a more detailed coverage of WebWork, I highly recommend WebWork in Action (Manning, 2005).

Let’s pretend that we had written the web layer of the RoadRantz application using WebWork instead of Spring MVC. In the Spring MVC version of RoadRantz, we wrote RantsForDayController (listing 13.8), which produced a list of rants that were posted on a particular month, day, and year. In the WebWork version, we would instead write RantsForDayAction, as shown in listing 16.4.

Example 16.4. A WebWork implementation of a RoadRantz feature

For the most part, RantsForDayAction represents a typical WebWork action implementation. Instead of being given request parameters through a command object as with Spring’s command controllers or through an ActionForm as in Struts, WebWork actions are given request parameters through properties. In RantsForDayAction, the month, day, and year properties are expected to have been populated from request parameters by the time that the execute() method is invoked.

The execute() method is where all of the action takes place. Here, the month, day, and year properties are pulled together to construct a Date, which is then used to retrieve a list of rants from the rantService property.

The setRantService() method is the clue as to how rantService is set. The presence of this method indicates that RantsForDayAction expects the rantService property to be injected with some implementation of RantService. The big question is how we can get Spring to inject into a WebWork action.

Although there are a number of ways to integrate Spring with WebWork, depending on which version of WebWork you’re using, we will be focusing our attention on the latest version of WebWork—version 2.2.3.

Originally, the WebWork project maintained its own dependency injection container. But as of WebWork 2.2, the WebWork team has deprecated their own DI container in favor of Spring. Because of the WebWork team’s commitment to Spring, integrating Spring with WebWork involves only three very simple steps:

  1. Add the Spring JARs to the application’s classpath.

  2. Configure a Spring ContextLoaderListener in web.xml.

  3. Configure WebWork to use Spring as an object factory.

The first step is almost self-explanatory. Just make sure that spring.jar and any other JARs that your Spring beans require are available in the /WEB-INF/lib directory of the deployed web application so that WebWork can use them.

We talked about how to configure a ContextLoaderListener in chapter 13. For a refresher, turn back to section 13.1.2 to see how ContextLoaderListener loads a Spring application context.

The last step is a simple matter of adding the following line to the webwork.properties file:

webwork.objectFactory=spring

For Struts 2 projects, the configuration is very similar, with only a slight change. The configuration file is struts.properties and the line should read as follows:

struts.objectFactory=spring

This line of configuration tells WebWork/Struts 2 to try to retrieve objects from the Spring container before creating them itself. This means that whenever WebWork needs an instance of an action class, it will first look in the Spring application context for the action, as shown in figure 16.3. If it’s found then it will use the Spring-managed action. Otherwise, WebWork will instantiate the action itself.

Figure 16.3. By using the spring object factory, WebWork/Struts 2 will retrieve its action classes from the Spring application context instead of trying to create them itself.

In the case of RantsForDayAction, we’ll configure it in the Spring application context like this:

<bean id="rantsForDayAction"
    class="com.roadrantz.webwork.RantsForDayAction"
    scope="prototype">
  <property name="rantService" ref="rantService" />
</bean>

Notice that the scope attribute has been set to prototype. That’s because Web-Work expects a fresh instance of the action to handle each request. That makes WebWork actions function very much like Spring MVC’s throwaway controllers. In fact, if you compare the code from listing 16.4 with the throwaway controller implementation in listing 13.8, you’ll find that they’re not very different.

On the WebWork, side, we’ll need to declare an <action> element in xwork.xml:

<action name="rantsForDay" class="rantsForDayAction">
  <result>dayRants.jsp</result>
</action>

Unlike a typical WebWork <action> configuration, however, the class attribute doesn’t contain a class name at all. Instead, it is configured with the name of the Spring-managed bean that holds the action implementation. When WebWork has to handle the rantsForDay action, it will ask Spring for an instance of the bean declared with the name rantsForDayAction.

Although Struts and WebWork are significant web frameworks, a new breed of component-based web frameworks is capturing the interest of Java web developers. One of the most intriguing of these is Tapestry, which uses plain HTML instead of JSP as its template language. Let’s see how to use Spring in a Tapestry application.

Integrating Spring with Tapestry

Tapestry is another MVC framework for the Java platform that is quite popular. One of the most appealing features of Tapestry is that instead of relying on JSP, Velocity, or some other templating solution, Tapestry uses plain HTML as its template language.

While it may seem peculiar that Tapestry uses a static markup language to drive dynamically created content, it’s actually a practical choice. Tapestry components are placed within an HTML page using any HTML tag you want to use (<span> is often the tag of choice for Tapestry components). The HTML tag is given a jwcid attribute, which references a Tapestry component definition. For example, consider the following simple Tapestry page:

<html>
  <head><title>Simple page</title></head>
  <body>
    <h2><span jwcid="simpleHeader">Simple header</span></h2>
  </body>
</html>

When Tapestry sees the jwcid attribute, it will replace the <span> tag (and its content) with the HTML produced by the simpleHeader component. The nice thing about this approach is that page designers and Tapestry developers alike can easily understand this HTML template. Even without being processed by the Tapestry engine, Tapestry templates load cleanly into any HTML design tool or browser.

In this section, we’re going to look into how to use Spring and Tapestry together so that Tapestry pages and components can use Spring-managed beans. We’re going to assume that you are already familiar with Tapestry. If you need to learn more about Tapestry, I recommend Tapestry in Action (Manning, 2004).

There are actually two different ways of integrating Spring with Tapestry, depending on whether you are using Tapestry 3 or Tapestry 4. Because there are still a lot of projects being developed on Tapestry 3, we’ll start by briefly covering the integration of Spring with Tapestry 3 before looking into the latest integration with Tapestry 4.

Integrating Spring with Tapestry 3

Tapestry’s engine maintains an object (known as global) that is a simple container for any objects you want shared among all Tapestry sessions. It is a java.util.HashMap by default.

The key strategy behind Tapestry-Spring integration is loading a Spring application context into Tapestry’s global object. Once it’s in global, all pages can have access to Spring-managed beans by retrieving the context from global and calling getBean().

To load a Spring application context into Tapestry’s global object, you’ll need to replace Tapestry’s default engine (org.apache.tapestry.engine.Base-Engine) with a custom engine. Unfortunately, Spring does not come with such a replacement Tapestry engine. This leaves it up to us to write it for ourselves (even though it’s virtually the same for any Spring/Tapestry hybrid application).

SpringTapestryEngine (listing 16.5) extends BaseEngine to load a Spring application context into the Tapestry global property.

Example 16.5. A replacement Tapestry engine that loads a Spring context into global

SpringTapestryEngine (see figure 16.4) first checks global to see if the Spring context has already been loaded. If so then there is nothing to do. But if global doesn’t already have a reference to the Spring application context, it will use WebApplicationContextUtils to retrieve a web application context. It then places the application context into global, under the name springContext, for later use.

Figure 16.4. SpringTapestryEngine, which replaces Tapestry’s default engine, places the Spring application context into global, thereby making the entire Spring context available to all Tapestry pages and components.

Because SpringTapestryEngine uses WebApplicationContextUtils to look up the application context, you’ll need to be sure to load the context into your web application’s servlet context using ContextLoaderListener. Refer to section 13.1.2 in chapter 13 for details on using ContextLoaderListener.

Note that there is one limitation of SpringTapestryEngine as it is written. It assumes that the global object is a java.util.Map object. This is usually not a problem as Tapestry defaults global to be a java.util.HashMap. But if your application has changed this by setting the org.apache.tapestry.global-class property, SpringTapestryEngine will need to change accordingly.

The last thing to do is to replace the default Tapestry engine with SpringTapestryEngine. This is accomplished by configuring the engine-class attribute of your Tapestry application:

<application name="RoadRantz"
    engine-class="com.springinaction.tapestry.SpringTapestryEngine">
...
</application>

At this point, the Spring application context is available in Tapestry’s global object, ready to be used to dispense Spring-managed service beans. Let’s have a look at how to wire those service beans into a Tapestry page specification.

Loading Spring beans into Tapestry pages

Suppose that we had implemented the web layer of the RoadRantz application using Tapestry instead of Spring MVC. In a Tapestry application, all pages have a page class that defines the functionality of the page. In a Tapestry implementation of RoadRantz, the homepage may be backed by the HomePage class in listing 16.6.

Example 16.6. A Tapestry 3 version of the RoadRantz homepage

The key thing to note in listing 16.6 is that HomePage uses a RantService to retrieve the list of recent rant entries. But where does this RantService come from? To understand how HomePage gets a RantService, you first must understand how Tapestry populates page properties.

You’ve no doubt noticed that the getRantService() method is abstract, which implies HomePage must be subclassed for getRantService() to be given a concrete implementation. But don’t worry—you won’t have to subclass HomePage. Tapestry will handle that for you.

When populating page properties, Tapestry uses a form of getter injection. Much like Spring’s getter injection capabilities that we discussed in chapter 3, when Tapestry loads HomePage, it will create a subclass of it with a concrete implementation of getRantService() that will return a specific value.

The value that will be returned by getRantService() will be decided by HomePage’s page specification—home.page:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification
    PUBLIC "-//Apache Software Foundation//
      Tapestry Specification 3.0//EN"
    "http://jakarta.apache.org/tapestry/dtd/Tapestry_3_0.dtd">

<page-specification
    class="com.roadrantz.tapestry.HomePage">
  <property-specification name="rantService"
       type="com.roadrantz.service.RantService">
     global.springContext.getBean("rantService")
  </property-specification>

  <property-specification name="rant"
       type="com.roadrantz.domain.Rant"/>
</page-specification>

The <property-specification> entry in home.page tells Tapestry to retrieve the value of the rantService property by calling getBean() on the Spring context that was placed in global. Effectively, Tapestry will implement the getRantService() method of HomePage to return the value in the <property-specification> element.

Now that Tapestry knows how to implement the getRantService() method, it can be used in the getRants() method to retrieve a list of recently posted rants. Consequently, the list of rants can be displayed by placing the following in home.html:

<ul>
  <span jwcid="@Foreach" source="ognl:rants"
      value="ognl:rant">
    <li><span jwcid="@Insert" value="rant.vehicle.state"/>
        <span jwcid="@Insert" value="rant.vehicle.plateNumber"/> --
        <span jwcid="@Insert" value="rant.rantText "/></li>
  </span>
</ul>

A nice thing about how the Tapestry-Spring integration works is that neither the Tapestry page class nor the HTML template is aware that Spring is involved. The only place where Spring is mentioned is in the page specification file.

Now that you’ve seen how to integrate Spring into a Tapestry 3 application, let’s move forward a bit and see how Spring can work with the latest version of Tapestry, Tapestry 4.

Integrating Spring with Tapestry 4

Tapestry 3 is a fantastic framework for building web applications. But if you’ve worked with it much, you know that it demands that you write quite a bit of XML in the form of page and component specifications. Among other improvements, Tapestry 4 takes exploits Java 5 annotations to greatly reduce the amount of XML required in a Tapestry-based application.

Moreover, integrating Spring into a Tapestry 4 application has been greatly simplified, thanks largely to a project called Diaphragma. According to its homepage, Diaphragma’s aim is to “provide many excellent components and extensions for [the] Tapestry framework.” At the time I’m writing this, however, the only extension offered by Diaphragma is one that simplifies integration with Spring. As fortune would have it, that’s exactly what we need!

The first step to using Spring in your Tapestry 4 applications is to download the tapestry-spring.jar file from the Diaphragma page on SourceForge: http://sourceforge.net/projects/diaphragma. Once you have the JAR file, make sure that it’s in your application’s classpath by placing it in the /WEB-INF/lib directory of the deployed application.

Next you’ll need to make sure that you have the Spring application context loaded. Just as with integrating Spring and Tapestry 3, this involves placing a ContextLoaderListener in the application’s web.xml file. Refer to section 13.1.2 in chapter 13 for details on how to configure ContextLoaderListener.

Now you’re ready to start injecting Spring-configured beans into your Tapestry pages and components. The HomePage class in listing 16.7 illustrates the easiest way to do this.

Example 16.7. Using annotations to inject Spring beans into a Tapestry page

You’ll notice that there’s virtually no difference between listing 16.7 and the Tapestry 3 version in listing 16.6. In fact, Tapestry 3 isn’t much different from Tapestry 4 with regard to developing page-backing classes.

However, notice that there’s one small addition to listing 16.7. The getRantService() method has been annotated with @InjectObject. This annotation tells Tapestry that the getRantService() should be injected with some object. The value given to the annotation specifies the name of the object to be injected. In this case, the object name is prefixed with the spring namespace, indicating that Tapestry should retrieve the rantService bean from the Spring application context and use it as the return value of getRantService().

Just because Tapestry 4 encourages the use of Java 5 annotations, that doesn’t mean you’re out of luck if you’ve not made the move to Java 5 yet. Instead of using the @InjectObject annotation in the page-backing class, you can optionally configure the injection of getRantService() in the page specification XML file:

<page-specification
    class="org.apache.tapestry.html.BasePage">
...
  <inject property="rantService"
      object="spring:rantService" />
...
</page-specification>

The <inject> element performs the same job as the @InjectObject annotation. That is, it injects a property with some value. In this case, the rantService property (represented by the abstract getRantService() method) will be injected with the rantService bean from the Spring application context.

Tapestry is just one of several component-based web application frameworks. JavaServer Faces (JSF) is another such framework that carries the distinction of being defined as a Java standard. Coming up in the next section, we’re going to see how to use Spring with JSF.

Putting a face on Spring with JSF

Relatively speaking, JavaServer Faces (JSF) is a newcomer in the space of Java web frameworks. But it has a long history. First announced at JavaOne in 2001, the JSF specification made grand promises of extending the component-driven nature of Swing and AWT user interfaces to web frameworks. The JSF team produced virtually no results for a very long time, leaving some (including me) to believe it was vaporware. Then in 2002, Craig McClanahan (the original creator of Jakarta Struts) joined the JSF team as the specification lead and everything turned around.

After a long wait, the JSF 1.0 specification was released in February 2004 and was quickly followed by maintenance 1.1 specification in May 2004 and another specification update in August 2005. Now JSF is being promoted as the standard Java web development platform and has been adopted by a large number of developers. With so much attention being given to JSF, it would seem appropriate for integration solutions to exist for JSF and Spring.

If you’re already familiar with JSF, you know that JSF has built-in support for dependency injection. You may be wondering why you should bother integrating Spring into JSF. It’s true that JSF’s support for setter injection is not all that different from that of Spring. But remember that Spring offers more than just simple dependency injection. Spring can bring a lot of value-add to JSF. Spring’s other features (such as declarative transactions, security, remoting, etc.) could come in handy in a JSF application.

Furthermore, even though JSF is intended as a presentation layer framework, service- and data access–layer components are frequently declared in a JSF configuration file. This seems somewhat inappropriate. Wouldn’t it be better to separate the layers, making JSF responsible for presentation stuff and Spring responsible for the rest of the application?

In this section, I’ll show you how to expose Spring-managed beans to JSF. I’m assuming that you are already familiar with JSF. If you are new to JSF or just need a refresher, I recommend that you have a look at JavaServer Faces in Action (Manning, 2004).

Before I can show you how Spring integrates with JSF, it’s important to understand how JSF resolves variables on its pages without Spring. Therefore, let’s have a quick look at how JSF works without Spring and then we’ll see how Spring can be used to provide beans for use in JSF pages.

Resolving JSF-managed properties

Imagine that before you had ever heard of Spring, you had already developed the RoadRantz application using JSF in the web layer. As part of the application, you have created a form that is used to register new motorists. The following excerpt from the JSF-enabled registerMotorist.jsp file shows how JSF binds the variables of a Motorist object to the fields in the form:

<h:form>
  <h2>Register Motorist</h2>
  <h:panelGrid columns="2">
    <f:verbatim><b>E-mail:</b></f:verbatim>
    <h:inputText value="#{motorist.email}" required="true"/>
    <f:verbatim><b>Password:</b></f:verbatim>
    <h:inputText value="#{motorist.password}" required="true"/>

    <f:verbatim><b>First Name:</b></f:verbatim>
    <h:inputText value="#{motorist.firstName}" required="true"/>

    <f:verbatim><b>Last Name:</b></f:verbatim>
    <h:inputText value="#{motorist.lastName}" required="true"/>
....
  </h:panelGrid>
  <h:commandButton id="submit" action="#{motorist.register}"
      value="Register Motorist"/>
</h:form>

Notice that the action parameter of the <h:commandButton> is set to #{motorist.register}. Unlike many other MVC frameworks (including Spring’s MVC), JSF doesn’t use a separate controller object to process form submissions. Instead, JSF passes control to a method in the model bean. In this case, the model bean is a Motorist. When the form is submitted, JSF will call the register() method of the motorist bean to process the form. The register() method is defined as follows:

public String register() {
  try {
    rantService.addMotorist(this);
  } catch (Exception e) {
    return "error";
  }

  return "success";
}

To keep the Motorist bean as simple as possible, the register() method simply delegates responsibility to the addMotorist() method of a RantService implementation. But where does the rantService property get set?

That’s a very good question. Internally, JSF uses a variable resolver to locate beans that are managed within the JSF application. The default JSF variable resolver looks up variables declared within the JSF configuration file (faces-config.xml). More specifically, consider the following declaration of the motorist bean in faces-config.xml for the answer to where the rantService property comes from:

<managed-bean>
  <managed-bean-name>motorist</managed-bean-name>
  <managed-bean-class>
    com.roadrantz.domain.Motorist
  </managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
    <property-name>rantService</property-name>
    <value>#{rantService}</value>
  </managed-property>
</managed-bean>

Here the motorist bean is declared as a request-scoped JSF-managed bean. But take note of the <managed-property> element. JSF supports a simple implementation of setter injection. #{rantService} indicates that the rantService property is being wired with a reference to a bean named rantService.

In a conventional JSF application (e.g., one where Spring is not involved), the rantService bean would be declared in faces-config.xml as a JSF-managed bean:

<managed-bean>
  <managed-bean-name>rantService</managed-bean-name>
  <managed-bean-class>
    com.roadrantz.service.RantServiceImpl
  </managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
  <managed-property>
    <property-name>rantDao</property-name>
    <value>#{rantDao}</value>
  </managed-property>
</managed-bean>

Once again, dependency injection is employed in the rantService bean—the rantDao property is wired with a reference to a bean named rantDao. And if we were to continue this exploration of faces-config.xml, we’d find that the rant-Dao bean is injected with a javax.sql.DataSource, which itself is also a JSF-managed bean.

Now that you know how JSF resolves variables on its pages without Spring, let’s add Spring to the mix so that non-presentation-layer beans can be managed in Spring and take advantage of the full gamut of Spring features.

Resolving Spring beans

For integration with Spring, we would like JSF to resolve its variables from the Spring application context. To do that, we’ll need to replace the default JSF variable resolver with a Spring-aware variable resolver.

Spring’s DelegatingVariableResolver is just such a variable resolver. Rather than resolve variables only from among JSF’s managed beans, DelegatingVariableResolver also looks in the Spring application context. It is configured in faces-config.xml like this:

<application>
  <variable-resolver>
    org.springframework.web.jsf.DelegatingVariableResolver
  </variable-resolver>
</application>

When JSF needs to resolve a variable, DelegatingVariableResolver will first look to the original variable resolver. If a JSF-managed bean can be found that fits the bill then it will be used. If not, however, DelegatingVariableResolver will then turn to the Spring application context to see if it can find a bean whose name matches the JSF variable name.

For DelegatingVariableResolver to be able to resolve variables as Spring-managed beans, we’ll need to make sure that the Spring application context is loaded. To do that we’ll need to configure a ContextLoaderListener in the application’s web.xml file. For more information on how to configure ContextLoaderListener, please turn to section 13.1.2 in chapter 13.

With DelegatingVariableResolver in place and the application context loaded, you are now ready to wire your service and data access layer beans in Spring and access them from JSF.

Using Spring beans in JSF pages

DelegatingVariableResolver makes the resolving of Spring-managed beans transparent in JSF. To illustrate, recall that the JSF-managed motorist bean is injected with a reference to the rantService bean using the following <managedproperty> declaration in faces-config.xml:

<managed-property>
  <property-name>rantService</property-name>
  <value>#{rantService}</value>
</managed-property>

Even though the rantService bean is now going to reside in the Spring context, nothing needs to change about the existing declaration of the motorist bean. When it comes time to inject the rantService property of the motorist bean, it asks DelegatingVariableResolver for the reference to the rantService bean. DelegatingVariableResolver will first look in the JSF configuration for the bean. When it can’t find it, it will then look in the Spring application context, as shown in figure 16.5.

Figure 16.5. After plugging Spring’s DelegatingVariableResolver into JSF, JSF variables are resolved from Spring beans if they can’t be found in the JSF configuration.

But it will only find the rantService bean in the Spring context if you declare it there. So, instead of registering the rantService bean as a <managed-bean> in faces-config.xml, place it in the Spring context definition file as follows:

<bean id="rantService"
    class="com.roadrantz.service.RantServiceImpl">
  <property name="rantDao" ref="rantDao" />
</bean>

Notice that this declaration of rantService is no different from how it would be declared in an application that uses Spring MVC. In fact, from the service layer to the data access layer, you will declare all of your application beans in the Spring application context exactly the same as you would if your application were fronted by Spring MVC. DelegatingVariableResolver will find them as though they are part of the JSF configuration.

Resolving Spring beans as JSF variables is the key part of the JSF-Spring integration. But maybe you don’t want to pick and choose Spring beans to expose as JSF-managed beans. Instead, it may be more convenient for JSF to have wholesale access to the Spring application context. For that, let’s look at another JSF variable resolver that lets JSF access the Spring application context itself.

Exposing the application context in JSF

It’s worth mentioning that Spring offers another option for integration with JSF. Whereas DelegatingVariableResolver transparently resolves JSF variables from among all of the beans in a Spring application context, WebApplicationContextVariableResolver only resolves one variable.

What good can WebApplicationContextVariableResolver possibly be if it only resolves a single variable? Well, it can be very handy if that one variable is the Spring application context itself!

You can configure JSF to use WebApplicationContextVariableResolver by placing the following <variable-resolver> entry in faces-config.xml:

<variable-resolver>
  org.springframework.web.jsf.WebApplicationContextVariableResolver
</variable-resolver>

With WebApplicationContextVariableResolver in place, your JSF pages have direct access to the Spring application context through a variable named webApplicationContext.

We’ve seen how Spring integrates into four of the most popular web frameworks. This opens up our set of options for web development with Spring, making it possible to choose the most appropriate web framework for the project. Now we’ll turn our attention to the very exciting topic of Ajax, and how to expose Spring objects.

Ajax-enabling applications in Spring with DWR

Traditionally, web applications have involved a back-and-forth conversation between the user and the server. The user sends a request to the server by either clicking a link or submitting a form. The server responds in kind by returning a page that represents the state of the application after the request is processed.

Contrasted with rich desktop applications, browser-based applications have been much less dynamic. Where a desktop application can update individual components as needed to communicate the state of the application with the user, web applications have only been able to respond a page at a time. This is a throwback to the browser’s original purpose: to display documents.

The web browser has come a long way since the days of the National Center for Supercomputing Applications (NCSA) Mosaic. In addition to displaying simple documents, modern web browsers are capable of dynamic behavior. For several years, Dynamic HTML (DHTML), the marriage of JavaScript, HTML, and Cascading Style Sheets, has enabled flashier looking web applications that mimic desktop application behavior.

Nevertheless, even though these DHTML applications present themselves as more dynamic than traditional web applications, until recently they were still page based and required a round-trip to the server to set and retrieve application state. While DHTML could dynamically alter the appearance of a web page, the missing piece was still a way to communicate with the server without reloading the web page.

In recent years, however, there has been a near revolution in how browser-based applications are developed. In the 5.0 version of Internet Explorer, Microsoft included an ActiveX component called XMLHttpRequest. XMLHttpRequest can be used with JavaScript to communicate with the server in XML messages. This technique for browser-to-server communication has been named Ajax, or Asynchronous JavaScript and XML.

When Ajax is mixed with DHTML, very dynamic web applications can be built that blur the lines between desktop applications and web applications. A browserbased application can communicate with the server using Ajax and then use DHTML to alter the page’s appearance to reflect the current application state. Google’s Gmail, Calendar, and Docs applications are prime examples of how Ajax can be used to build extremely dynamic web applications.

Even though XMLHttpRequest got its start in Internet Explorer 5.0, it’s now a common implement in all modern web browsers, including Internet Explorer, Mozilla Firefox, and Safari. This means that Ajax-capable web applications can be developed to target virtually every browser on every desktop.

Because they’re often used in tandem, many developers have lumped DHTML and Ajax together under the umbrella name of Ajax. But at its core, Ajax is a mechanism for communication between a web page and the server. In this section we’re going to focus our attention on the communication aspect of Ajax, looking at how to use an Ajax toolkit known as DWR to expose Spring beans as Ajaxremoted objects.

If you’d like to read more about Ajax, then there’s no shortage of books on the topic. I recommend that you have a look at Ajax in Action (Manning, 2006), Prototype and Scriptaculous in Action (Manning, 2007), Ajax in Practice (Manning, 2007), or Pragmatic Ajax (Pragmatic Bookshelf, 2006).

Direct web remoting

Almost as quickly as Ajax became the buzziest of buzzwords in software development, several frameworks emerged to simplify Ajax development. Many of them focus on providing DHTML widgets for dressing up web applications. But one framework, known as DWR, has stayed true to the central purpose of Ajax: communication between a web page and the server.

DWR, which is short for Direct Web Remoting, is a Java-based Ajax framework that lets you access virtually any server-side Java object through JavaScript. DWR abstracts XMLHttpRequest away so that invoking methods on a server-side Java object is as simple as invoking methods on a client-side JavaScript object.

In a sense, DWR is more of a remoting technology, akin to the remoting options presented in chapter 8, than it is a web technology. What separates DWR from those other remoting technologies is where its client resides. Whereas the clients of RMI, HTTP invoker, Hessian, and Burlap are other applications, the client of a DWR-exported object is JavaScript within a web page.

You can learn more about DWR from its homepage at https://dwr.dev.java.net. From there you can download the latest version of DWR. For the examples in this chapter, I’m using DWR 2.0.[1]

We’ll look at several ways of using DWR to export Spring beans to be invoked from JavaScript. But before we look at the DWR-Spring connection, let’s start with the basics. To set the stage for DWR and Spring integration, let’s begin by configuring DWR sans Spring.

Basic DWR configuration

In its most basic configuration, DWR exports remote Java objects to JavaScript through a servlet—DwrServlet to be precise. Therefore, the first thing we’ll need to do is add DwrServlet to web.xml using the following <servlet> and <servlet-mapping> entries:

<servlet>
  <servlet-name>dwr</servlet-name>
  <servlet-class>
    org.directwebremoting.servlet.DwrServlet
  </servlet-class>
  <init-param>
     <param-name>debug</param-name>
     <param-value>true</param-value>
    </init-param>
</servlet>

<servlet-mapping>
  <servlet-name>dwr</servlet-name>
  <url-pattern>/dwr/*</url-pattern>
</servlet-mapping>

As configured here, the DwrServlet will respond to requests whose URL, relative to the application’s context path, is /dwr/*. This is important to remember, because it is through this URL that we’ll load JavaScript code generated by DWR.

Also, notice that DwrServlet is configured with an <init-param> named debug set to true. This setting turns on a helpful debugging feature that we’ll use a little later. For production applications, however, you’ll want to remove this parameter or set it to false.

Defining the remote objects

Now that DwrServlet is set up, we’re ready to export some objects to JavaScript. But what objects should we export?

To demonstrate DWR’s capabilities, let’s add a traffic report feature to the RoadRantz application. After all, cranky motorists might be a bit less cranky if they could only check traffic conditions before they hit the road!

The user interface for the traffic report feature should prompt the user for a zip code and produce a list of traffic incidents in or near the zip code. To further constrain the results, the user should also be able to specify a zoom factor (how large of an area relative to the zip code to include in the search) and a minimum incident severity.

At the heart of the traffic report feature is TrafficServiceImpl (listing 16.8), a service class whose getTrafficInfo() method accepts a zip code, a zoom factor, and a severity and produces a list of traffic incidents.

Example 16.8. A traffic service implementation that looks up traffic data from an RSS feed

As you can see, the real magic behind the getTrafficInfo() method is that it refers to an RSS feed produced by Yahoo! to get its traffic data. We’re using the Rome RSS reader (http://rome.dev.java.net) to read and parse the feed. Yahoo!’s traffic data RSS feed is a free service, but it does require that you provide a valid application ID, configured in TrafficServiceImpl through the appId property. By default, TrafficServiceImpl will use springinaction as its application ID (which is a valid ID).

What’s most remarkable about TrafficServiceImpl is that it has no idea that we’re about to use it as a remote object that will be accessed from JavaScript. It’s just a POJO. But as you’ll soon see, its destiny involves Ajax and will be influenced by DWR.

The parameters to getTrafficInfo() are simple types. The return value, on the other hand, is a bit more complex. It is an array of TrafficInfo objects. The TrafficInfo class is a simple two-property bean, as shown in listing 16.9.

Example 16.9. A TrafficInfo object contains summary and detail information about a traffic incident

package com.roadrantz.traffic;

public class TrafficInfo {
  private String summary;
  private String details;

  public TrafficInfo() {}

  public String getDetails() { return details; }
  public void setDetails(String details) {
    this.details = details;
  }

  public String getSummary() { return summary; }
  public void setSummary(String summary) {
    this.summary = summary;
  }
}

In a traditional web application, built with Spring MVC, the user would enter this information into a form and submit it to a Spring MVC controller. The controller, which would be wired with a reference to TrafficServiceImpl, would call the getTrafficInfo() method to get an array of TrafficInfo items. Then it would package the array in a ModelAndView object and send it to a JSP to render a new page in the browser showing a list of traffic incidents.

But this isn’t a traditional web example. We’re talking about Ajax and we’d prefer to not load a completely new page in the browser. Instead, we’d like for the list of traffic incidents to appear on the same page where the search criteria are entered. To accomplish that, we’ll need DWR to export the TrafficServiceImpl class as a JavaScript object.

Exporting the remote objects to JavaScript

The principal means of configuring DWR is by adding a file named dwr.xml in the WEB-INF directory of the web application. Within dwr.xml we, among other things, tell DWR about the Java classes that we want exported to JavaScript.

Listing 16.10 shows the dwr.xml file that we’ll use to export the TrafficServiceImpl class to JavaScript.

Example 16.10. Exporting TrafficServiceImpl to JavaScript using a new creator in dwr.xml

The first thing to notice in listing 16.10 is a <convert> element. DWR is able to translate simple Java types (such as String and int) into equivalent types in JavaScript, but complex types such as TrafficInfo are a bit more difficult. Therefore, we’ve configured a “bean” converter to tell DWR to treat TrafficInfo as a basic JavaBean when translating it into a JavaScript type. This simply means that the JavaScript version of TrafficInfo will have the same properties as the Java version.

The main item of interest in listing 16.10 is the <create> element. <create> is used to tell DWR to expose a Java class as a remote object in JavaScript. Here we’re using a basic new creator, which essentially means that the remote service will be created by instantiating the class specified by the class parameter. Because we want to be able to access TrafficServiceImpl in JavaScript, we’ve set the value of the class parameter to the fully qualified class name. On the JavaScript side, the remote object will be known as Traffic, as configured through the javascript attribute.

The only other important item is the <exclude> element within <create>. While we want the JavaScript client to be able to invoke the getTrafficInfo() method on TrafficServiceImpl, we do not want the client to be able to set the application ID. Therefore, we’ve used <exclude> to exclude the setAppId() method from being exposed to JavaScript.

As configured, the DwrServlet will make the TrafficServiceImpl available as a remote Ajax object, with the getTrafficInfo() method available to JavaScript clients, as shown in figure 16.6.

Figure 16.6. DwrServlet makes POJOs available as remote Ajax objects that can be invoked from JavaScript clients running in a web browser.

Speaking of the JavaScript client, we’ll need to add a few <script> elements to the HTML so that DWR and the exported objects are available to JavaScript. First, we’ll need to load the JavaScript that contains the DWR engine:

<script type='text/javascript'
    src='dwr/engine.js'></script>

Next, we’ll need to load our TrafficServiceImpl class, exported in JavaScript as Traffic:

<script type='text/javascript'
    src='dwr/interface/Traffic.js'></script>

Hold on... We haven’t written a JavaScript version of TrafficServiceImpl. Where is all of this JavaScript coming from? I’m glad you asked.

Notice that both engine.js and Traffic.js are loaded with paths that start with dwr/. This dwr/ is the very same dwr/ that the DwrServlet is mapped to in web.xml. That means that DwrServlet is serving those JavaScript files to the client.

In fact, Traffic.js is dynamically generated from the <create> element that we configured in dwr.xml. It contains code to create a JavaScript object called Traffic, which is nothing more than a client stub for the server-side TrafficServiceImpl object. In a moment, we’ll use the Traffic object to retrieve traffic information.

But first, there’s one more bit of JavaScript that we’ll choose to include:

<script type='text/javascript'
    src='dwr/util.js'></script>

DWR’s util.js is optional, but contains some very handy functions for performing DHTML tasks. For the RoadRantz traffic report feature, we’ll use a function from util.js to dynamically populate an HTML <table> with rows of traffic incident data.

Calling remote methods from JavaScript

On the client side, we’re going to ask the user to enter a zip code and to optionally choose a zoom factor and a minimum severity. These criteria are entered using an <input> and two <select>s. The following fragment of HTML shows the relevant portion of the user interface:

<input type="text" name="zip" maxlength="5"
    onkeyup="criteriaChanged();"/>
...
<select name="zoom" onchange="criteriaChanged();">
    ...
</select>
...
<select name="severity" onchange="criteriaChanged();">
    ...
</select>

Rather than have the user click a submit button to trigger a traffic incident query, we want the UI to respond immediately to changes in the criteria fields. Therefore, the <input> field’s onkeyup event and both of the <select>s’ onchange event have been set to call a criteriaChanged() JavaScript function. Within criteriaChanged(), we’ll invoke the remote getTrafficInfo() method:

function criteriaChanged() {
  var zipCode = document.trafficForm.zip.value;
  var zoom = document.trafficForm.zoom.value;
  var severity = document.trafficForm.severity.value;

  if(zipCode.length == 5) {
    Traffic.getTrafficInfo(zipCode, zoom, severity,
        updateTable);
  } else {
    DWRUtil.removeAllRows("trafficTable");
  }
}

Here’s where the proverbial rubber meets the road... or more precisely, where JavaScript meets DWR. If the length of the value in the zip code field is 5 characters (a fully specified zip code) then we use the Traffic object created in Traffic.js to call the getTrafficInfo() method. Under the covers, getTrafficInfo() uses the DWR engine to construct an XMLHttpRequest to call the getTrafficInfo() on the server-side TrafficServiceImpl object.

When we call getTrafficInfo(), we pass it the zip code, the zoom factor, the severity, and... wait a minute. There’s an extra parameter in there. What’s updateTable and how did it get into our call to getTrafficInfo?

Remember that DWR is an Ajax framework, and that the “A” in Ajax means “asynchronous.” In other words, when a call is made to a remote method, the browser doesn’t stop everything and wait for a response. This is important, because the remote object could take a moment or two to respond and we don’t want the user experience to be ruined because their web browser froze up.

Even though the browser doesn’t stop and wait for a response, we still need a way to get the response from the remote call. For that, we need a callback function. In our case, updateTable is the name of the callback function. When the server finally responds with a list of TrafficInfo objects, the updateTable() function will be called to process the results.

Displaying the results

As you may have guessed from its name, the updateTable() function is used to populate a table with rows of traffic information. Before we see how updateTable() works, however, let’s see the table that it will be updating. The following HTML fragment shows the table that we’ll use to display traffic incident data:

<table width="100%" border="1" style="font-size:8pt;">
  <thead>
    <tr><td width="100">Summary</td><td>Details</td></tr>
  </thead>
  <tbody id="trafficTable"></tbody>
</table>

The key thing to pay attention to in the table definition is the id of the <tbody> element. We’ll refer to this id when populating the table.

Now here’s the updateTable() function, the dynamic part of this application:

function updateTable(results) {
  DWRUtil.removeAllRows("trafficTable");
  DWRUtil.addRows("trafficTable", results, cellFuncs);
}

updateTable() uses the DWRUtil object (created in util.js) to remove and add rows to the table of traffic incidents. The first thing it does is call removeAllRows() to clear out the table and prepare it for a fresh set of data. Then it populates the table by calling addRows(), passing in the results object that was passed to updateTable() by the DWR engine. results is the data that was returned from the call to getTrafficInfo()—specifically an array of JavaScript-ified TrafficInfo objects.

The final parameter to addRows() is a bit peculiar and demands some explanation. addRows() will add one row to the table for each TrafficInfo it finds in the results array. But how will it know how to populate each column in the table? That’s where the cellFuncs array comes in:

var cellFuncs = [
  function(data) { return data.summary; },
  function(data) { return data.details; }
];

cellFuncs is an array of functions, one for each column in the table. As DWRUtil’s addRows() populates the traffic incident table, it will pass in the current TrafficInfo object to each of the functions in cellFuncs to determine what value goes into each column. In this example, the first function in cellFuncs will be used to populate the summary column and the second function will be used to populate the details column.

That’s it! We now have a complete, albeit simple, DWR application. To recap, when the user enters data into the zip code, zoom factor, or severity fields, JavaScript will invoke the getTrafficInfo() function on the Traffic object. The Traffic object, being nothing more than a client stub for the remote Traffic-ServiceImpl object, uses DWR to produce an XMLHttpRequest to make a call to the server-side getTrafficInfo() method. When a response comes back, the updateTable() callback function will be called to populate the table with the traffic incident data.

This is fine, but where does Spring fit into the picture? Let’s find out.

Accessing Spring-managed beans DWR

So far, we’ve only used DWR’s plain-vanilla new creator to export the server-side TrafficServiceImpl class to the JavaScript client. The new creator is fine for simple applications where the exported class doesn’t have any dependencies on other objects. But because DWR is responsible for creating the remote object, it rules out any chance for them to be configured using Spring dependency injection or to be advised by Spring AOP. Wouldn’t it be better if DWR could export beans that are managed in the Spring application context?

Fortunately, there are a couple of options available for integration between Spring and DWR. These options include

  • You can use spring creators in DWR—as an alternative to the new creator we discussed before, DWR also comes with a spring creator that looks up beans that are to be remoted from the Spring application context.

  • If you’re using Spring 2.0, you can use DWR’s Spring 2.0 configuration namespace to declare beans as DWR-remoted.

We’re now going to revisit the RoadRantz traffic application, looking at each of these Spring integration options. Don’t worry, though... most of the work we’ve put into the traffic report application is still valid. The client side code will remain unchanged, as will the TrafficServiceImpl and TrafficInfo classes. We’ll only need to tweak the DWR configuration a bit to use Spring.

With that said, let’s start by looking at using DWR’s spring creator to export Spring-managed beans as remote objects in JavaScript.

Using Spring creators

Unlike the new creator, DWR’s spring creator doesn’t actually create objects to export as Ajax objects. Instead, the spring creator retrieves objects from the Spring application context. This way, the object can enjoy all of Spring’s dependency injection and AOP goodness. Figure 16.7 illustrates how this works.

Figure 16.7. Rather than create the remote services itself, DWR’s spring creator remotes objects loaded from the Spring application object.

Using the spring creator isn’t much different from using the new creator. In fact, we only need to make a few minor tweaks to the dwr.xml file to make the switch from new to spring:

  • The creator attribute needs to be changed from new to spring.

  • The class parameter is no longer appropriate. Therefore, we’ll need to change the <param> element’s name attribute from class to beanName. The beanName parameter is used to identify the Spring-managed bean to be exported.

  • Since we’re identifying a bean by its name, the fully qualified class name in <param>’s value attribute should be replaced with the name of a bean in the Spring application context.

After applying these changes, the <create> element in dwr.xml looks like this:

<create creator="spring" javascript="Traffic">
  <param name="beanName" value="traffic" />
  <exclude method="setAppId" />
</create>

Here we’re specifying that DWR should export a bean named traffic. The bean itself is declared in the Spring application context like this:

<bean id="traffic"
    class="com.roadrantz.traffic.TrafficServiceImpl">
  <property name="appId" value="springinaction" />
</bean>

Notice that the <bean> element’s id attribute corresponds to the beanName parameter’s value in dwr.xml.

Now that TrafficServiceImpl is declared as a Spring bean, we are able to configure and wire it using Spring-style dependency injection. In this case, we’re merely injecting the appId property with our Yahoo! application ID. Nevertheless, by configuring TrafficServiceImpl in Spring, it could be subjected to any of Spring’s capabilities, including AOP, declarative transactions, security through Acegi, and so forth.

One other benefit of configuring DWR-exposed objects in Spring is that it frees DWR from the knowledge of how the object is implemented. DWR only knows that it is to expose a bean named traffic as an Ajax service. It has no idea what class implements that bean. Although it is declared here as the concrete TrafficServiceImpl class, traffic could be a reference to a web service, an RMI service, an EJB, or any number of other possibilities.

The question that remains is how the Spring application context gets loaded. By default, the spring creator assumes that a Spring application context has been loaded using ContextLoaderListener. For example, the following snippet from the application’s web.xml loads the Spring context from a file called traffic.xml at the root of the application’s classpath:

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>
    classpath:traffic.xml
  </param-value>
</context-param>
<listener>
  <listener-class>
     org.springframework.web.context.ContextLoaderListener
  </listener-class>
</listener>

Optionally, we can explicitly tell the spring creator the name of a Spring application context file by setting the location parameter:

<create creator="spring" javascript="Traffic">
  <param name="beanName" value="traffic" />
  <param name="location" value="traffic.xml" />
  <exclude method="setAppId" />
</create>

When location is set, DWR will load the Spring application context from the file specified in the <param> element’s value attribute, relative to the application’s classpath.

Although the location parameter is convenient, it’d be unusual to use DWR’s spring creator within an application that hasn’t already used ContextLoaderListener to load a Spring application context for other purposes. Therefore, in most cases it’s best to forego the use of the location parameter and rely on Context-LoaderListener to load the application context.

DWR’s spring creator is great, but it has one small shortcoming: you must configure it in dwr.xml. This means that there’s one more configuration file for your application. Well, kiss that dwr.xml file goodbye, because we’re about to see how we can configure DWR completely within Spring.

Using DWR’s Spring configuration namespace

One of the most significant new features in Spring 2 is the ability to define custom configuration namespaces. Throughout this book, we’ve seen several ways that custom namespaces have been used to dramatically cut down on the amount of XML required to configure Spring.

Spring 2’s custom namespaces presented an opportunity to the DWR team. Rather than configure DWR in dwr.xml, completely separate from Spring’s configuration, why not provide a custom DWR namespace so that DWR can be configured completely within a Spring configuration file? That’s precisely what is provided in the latest version of DWR.

To use DWR’s configuration in Spring, the first thing we’ll need to do is swap out DwrServlet for DwrSpringServlet:

<servlet>
  <servlet-name>dwr</servlet-name>
  <servlet-class>
     org.directwebremoting.spring.DwrSpringServlet
  </servlet-class>
  <load-on-startup>1</load-on-startup>
</servlet>

Where DwrServlet used dwr.xml for determining what objects should be exported as Ajax objects, DwrSpringServlet looks directly at the Spring application context. Specifically, it looks for the elements defined in spring-dwr-2.0.xsd.

So that DWR’s configuration elements will be available in the Spring application file, we’ll need to add the dwr namespace to the <beans> element:

<?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:dwr=
      "http://www.directwebremoting.org/schema/spring-dwr"
  xsi:schemaLocation=
    "http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/
          spring-beans-2.0.xsd
     http://www.directwebremoting.org/schema/spring-dwr
     http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd">
...
</beans>

Now we can use <dwr:*> elements to declare certain beans to be exported as Ajax objects. The easiest way to declare a Spring bean as a DWR-exported Ajax object is to include the <dwr:remote> element within the <bean> declaration. For example, here’s the TrafficServiceImpl bean, declared to be DWR-remoted:

<bean id="traffic"
    class="com.roadrantz.traffic.TrafficServiceImpl">
  <dwr:remote javascript="Traffic">
    <dwr:exclude method="setAppId"/>
  </dwr:remote>
  <property name="appId" value="springinaction" />
</bean>

Voilà! Just by adding the <dwr:remote> element as a child of the <bean> element, we’ve told DWR to export the bean as a remote Ajax object. The javascript attribute specifies a name that the object will be known as in JavaScript.

We still want to hide the setAppId() method from the client, so we’ve also added a <dwr:exclude> element as a child of <dwr:remote>. By default, all public methods will be exposed, but we can exclude certain methods by listing their names (comma-separated) in <dwr:exclude>’s method attribute. Here we’ve specified that the setAppId() method should be excluded.

Optionally, we could’ve used <dwr:include> to explicitly declare the methods that are to be exported:

<bean id="traffic"
    class="com.roadrantz.traffic.TrafficServiceImpl">
  <dwr:remote javascript="Traffic">
    <dwr:include method="getTrafficInfo"/>
  </dwr:remote>
  <property name="appId" value="springinaction" />
</bean>

When <dwr:include> is used, only the methods listed in the method attribute (comma-separated) are exported in the remote Ajax object. In this case, we’re only interested in including the getTrafficInfo() method.

We need to tie up one more loose end. We still have to tell DWR how to translate the TrafficInfo type into its JavaScript equivalent. We did this with the <convert> element in dwr.xml. Since we don’t have a dwr.xml file anymore, we’ll use the <dwr:convert> element in the Spring configuration:

<dwr:configuration>
  <dwr:convert type="bean"
      class="com.roadrantz.traffic.TrafficInfo" />
</dwr:configuration>

Just like before, we’re specifying that the TrafficInfo object be translated to JavaScript using a bean converter. Notice that the <dwr:convert> element must be placed within a <dwr:configuration> element.

Summary

Even though Spring provides its own very capable web framework, you may find another framework more to your liking. Fortunately, choosing to use Spring in your service and data access layers doesn’t preclude the use of an MVC framework other than Spring MVC.

As you’ve seen in this chapter, Spring integrates into several prevalent MVC frameworks, including Struts, WebWork, Tapestry, and JavaServer Faces. As each of these frameworks is unique with respect to the others, the integration strategy is also different for each.

With Struts, you have two integration options. First, you can have your Struts actions become Spring aware, which provides a straightforward solution but couples your actions to Spring. Alternatively, you can have Struts delegate the handling of actions to Spring-managed beans, giving you a more loosely coupled solution, at the cost of a slightly more complex Struts configuration.

Integrating Spring into WebWork 2 couldn’t be any easier. WebWork 2 embraces Spring as its dependency injection container, so wiring WebWork actions with Spring beans involves little more than configuring a ContextLoader-Listener to make the Spring context available to WebWork. And because Struts 2 is really the same as WebWork 2, it’s just as easy to integrate Spring with Struts 2.

Tapestry is a great component-based web framework that uses simple HTML templates. Integrating Spring into Tapestry 3 involves replacing Tapestry’s base engine with a Spring-aware Tapestry engine. Integration with Tapestry 4, on the other hand, is much simpler—all you must do is add a JAR file from the Diaphragma project to your application’s classpath and then refer to Spring beans with a special spring: prefix.

Accessing Spring-configured beans in a JSF application involves registering a DelegatingVariableResolver, a special JSF variable resolver that is Spring aware. Optionally, you may also expose the entire Spring application context as a JSF variable by using WebApplicationContextVariableResolver.

The web development world is all abuzz about Ajax, a means of creating highly dynamic web applications where web pages can dynamically interact with the server without performing a full page refresh. To wrap up this chapter, we looked at Direct Web Remoting (DWR), an Ajax toolkit that makes it possible to transparently invoke methods on Spring-managed beans through JavaScript.

So now you know how to develop web applications using Spring with a variety of web frameworks. You have the option of using Spring’s MVC framework, or you can choose from one of several third-party frameworks. And you’ve also seen how to make your applications more dynamic with JavaScript remoting to Spring beans.



[1] As I write this, DWR 2.0 isn’t final, but I’m hopeful that it will be by the time you read this. I’m actually using DWR 2.0 milestone 4d.