Chapter 6. Integrating Spring with Other Web Frameworks

In this chapter, you will learn how to integrate the Spring framework with several popular web application frameworks, including Struts, JSF, and DWR. Spring's powerful IoC container and enterprise support features make it very suitable for implementing the service and persistence layers of your Java EE applications. However, for the presentation layer, you have a choice between many different web frameworks. So, you often need to integrate Spring with whatever web application framework you are using. The integration mainly focuses on accessing beans declared in the Spring IoC container within these frameworks.

Apache Struts (http://struts.apache.org/) is a popular open source web application framework based on the MVC design pattern. Struts has been used in many web-based projects in the Java community and thus has a large user base. Note that Spring's Struts support features target Struts 1.x only. This is because after Struts joined with WebWork in Struts version 2, it's very easy to configure Struts actions in Spring by using the Spring IoC container as the object factory of Struts 2.

JavaServer Faces, or JSF, (http://java.sun.com/javaee/javaserverfaces/) is an excellent component-based and event-driven web application framework included as part of the Java EE specification. You can use the rich set of standard JSF components and also develop custom components for reuse. JSF can cleanly separate presentation logic from UIs by encapsulating it in one or more managed beans. Due to its component-based approach and popularity, JSF is supported by a wide range of IDEs for visual development.

Direct Web Remoting, or DWR, (http://getahead.org/dwr) is a library that brings Ajax (Asynchronous JavaScript and XML) features to your web applications. It allows you to invoke Java objects on the server side by using JavaScript in a web browser. You can also update parts of a web page dynamically, without refreshing the entire page.

After finishing this chapter, you will be able to integrate Spring into web applications implemented with Servlet/JSP and popular web application frameworks such as Struts, JSF, and DWR.

Accessing Spring in Generic Web Applications

Problem

You would like to access beans declared in the Spring IoC container in a web application, regardless of which framework it uses.

Solution

A web application can load Spring's application context by registering the servlet listener ContextLoaderListener. This listener stores the loaded application context into the web application's servlet context. Later, a servlet, or any object that can access the servlet context, can also access Spring's application context through a utility method.

How It Works

Suppose you are going to develop a web application for users to find the distance (measured in kilometers) between two cities. First, you define the following service interface:

package com.apress.springrecipes.city;

public interface CityService {

    public double findDistance(String srcCity, String destCity);
}

For simplicity's sake, let's implement this interface by using a Java map to store the distance data. This map's keys are source cities while its values are nested maps that contain destination cities and their distances from the source city.

package com.apress.springrecipes.city;
...
public class CityServiceImpl implements CityService {

    private Map<String, Map<String, Double>> distanceMap;

    public void setDistanceMap(Map<String, Map<String, Double>> distanceMap) {
        this.distanceMap = distanceMap;
    }

    public double findDistance(String srcCity, String destCity) {
        Map<String, Double> destinationMap = distanceMap.get(srcCity);
        if (destinationMap == null) {
            throw new IllegalArgumentException("Source city not found");
        }
Double distance = destinationMap.get(destCity);
        if (distance == null) {
            throw new IllegalArgumentException("Destination city not found");
        }
        return distance;
    }
}

Next, you create the following directory structure for your web application. As this application requires access to the Spring IoC container, you have to put the required Spring .jar files in your WEB-INF/lib folder. If you are using Maven, see chapter 1 for information on adding the correct .jars to your CLASSPATH.

city/
    WEB-INF/
        classes/
        lib/-*.jar
        jsp/
            distance.jsp
        applicationContext.xml
        web.xml

In Spring's bean configuration file, you can hard-code some distance data for several cities using the <map> element. You create this file with the name applicationContext.xml and put it in the root of WEB-INF.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="cityService"
        class="com.apress.springrecipes.city.CityServiceImpl">
        <property name="distanceMap">
            <map>
                <entry key="New York">
                    <map>
                        <entry key="London" value="5574" />
                        <entry key="Beijing" value="10976" />
                    </map>
                </entry>
            </map>
        </property>
    </bean>
</beans>

In the web deployment descriptor (i.e., web.xml), you register the Spring-provided servlet listener ContextLoaderListener to load Spring's application context into the servlet context at startup. It will look for the context parameter contextConfigLocation for the location of the bean configuration file. You can specify multiple bean configuration files by separating them with either commas or spaces.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
</web-app>

Actually, the default location where this listener will look for a bean configuration file is exactly what you specified (i.e., /WEB-INF/applicationContext.xml). So, you can simply omit this context parameter.

To allow users to query distances between cities, you have to create a JSP file that contains a form. You can name it distance.jsp and put it in the WEB-INF/jsp directory to prevent direct access to it. There are two text fields in this form for users to input the source and destination cities. There's also a table grid for showing the actual distance.

<html>
<head>
<title>City Distance</title>
</head>

<body>
<form method="POST">
<table>
  <tr>
    <td>Source City</td>
    <td><input type="text" name="srcCity" value="${param.srcCity}" /></td>
  </tr>
  <tr>
    <td>Destination City</td>
    <td><input type="text" name="destCity" value="${param.destCity}" /></td>
  </tr>
  <tr>
    <td>Distance</td>
    <td>${distance}</td>
  </tr>
  <tr>
    <td colspan="2"><input type="submit" value="Find" /></td>
  </tr>
</table>
</form>
</body>
</html>

You need a servlet to process the distance requests. When this servlet is accessed with the HTTP GET method, it simply displays the form. Later, when the form is submitted with the POST method, this servlet finds the distance between the two input cities and displays it in the form again.

Note

To develop web applications that use the Servlet API, you have to include the Servlet API. If you are using Maven, add the following dependency to your project:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
 </dependency>
package com.apress.springrecipes.city.servlet;
...
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

public class DistanceServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        forward(request, response);
    }

    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        String srcCity = request.getParameter("srcCity");
        String destCity = request.getParameter("destCity");

        WebApplicationContext context =
            WebApplicationContextUtils.getRequiredWebApplicationContext(
                getServletContext());
        CityService cityService = (CityService) context.getBean("cityService");
        double distance = cityService.findDistance(srcCity, destCity);
        request.setAttribute("distance", distance);
forward(request, response);
    }

    private void forward(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        RequestDispatcher dispatcher =
            request.getRequestDispatcher("WEB-INF/jsp/distance.jsp");
        dispatcher.forward(request, response);
    }
}

This servlet needs to access the cityService bean declared in the Spring IoC container to find distances. As Spring's application context is stored in the servlet context, you can retrieve it through the WebApplicationContextUtils.getRequiredWebApplicationContext() method by passing in a servlet context.

Finally, you add this servlet declaration to web.xml and map it to the URL pattern /distance.

<web-app ...>
    ...
    <servlet>
        <servlet-name>distance</servlet-name>
        <servlet-class>
            com.apress.springrecipes.city.servlet.DistanceServlet
        </servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>distance</servlet-name>
        <url-pattern>/distance</url-pattern>
    </servlet-mapping>
</web-app>

Now, you can deploy this web application to a web container (e.g., Apache Tomcat 6.x). By default, Tomcat listens on port 8080, so if you deploy your application to the city context path, you can access it with the following URL once it has been started up:

http://localhost:8080/city/distance

Using Spring in Your Servlets and Filters

Problem

The servlet specification provides servlets and filters. Servlets handle requests and responses and are responsible ultimately for producing output and acting on the inbound request. Filters are given the opportunity to react to the state of a given request and response before and after the servlet to which a request is destined has processed it. Filters provide the same effect as aspect-oriented programming, which lets you intercept and modify the state of a method invocation. Filters can be added in arbitrary depths to any existing servlet. It is possible, thus, to reuse filters to provide generic functionality to any servlet, like for example gzip compression. These artifacts are declared in the web.xml file. The servlet container reads the configuration and instantiates the servlets or filters on your behalf and manages the life cycles of these objects inside the container. Because the life cycle is handled by the servlet container, and not by Spring, accessing the services of the Spring container—for example using traditional dependency injection and AOP—proves difficult. You can use WebApplicationContextUtils to look up and acquire the dependencies you need, but that defeats the value proposition of dependency injection—your code is required to acquire instances explicitly, which is not much better than using JNDI, for example. It is desirable to let Spring handle dependency injection for you and to let Spring manage the life cycles of your beans.

Solution

If you want to implement filter-like functionality but want to have full access to the Spring context's life cycle machinery and dependency injection, use the DelegatingFilterProxy class. Similarly, if you want to implement servlet-like functionality but want to have full access to the Spring context's life cycle machinery and dependency injection, use HttpRequestHandlerServlet. These classes are configured normally in web.xml, but they then delegate their obligations to a bean that you configure in the Spring application context.

How It Works

Servlets

Let's revisit our previous example. Suppose we wanted to rewrite the servlet functionality to leverage Spring's application context machinery and configuration. The HttpRequestHandlerServlet will handle this for us. It uses a little bit of indirection to achieve its work: you configure an instance of org.springframework.web.context.support.HttpRequestHandlerServlet in your web.xml and assign it a name. The servlet takes the name as configured in the web.xml file and looks up a bean in the root Spring application context. Assuming the bean exists and that it implements the HttpRequestHandler interface, the servlet delegates all requests to that bean by invoking the handleRequest method.

You must first write a bean that implements the org.springframework.web.HttpRequestHandler interface. We will endeavor to replace our existing DistanceServlet with a POJO that implements the HttpRequestHandler interface. The logic is identical; it's just been reorganized a bit. The POJO's definition is as follows:

package com.apress.springrecipes.city.servlet;

import com.apress.springrecipes.city.CityService;
import org.springframework.web.HttpRequestHandler;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class DistanceHttpRequestHandler implements HttpRequestHandler {
    private CityService cityService;

    public void setCityService(final CityService cityService) {
        this.cityService = cityService;
    }

    @Override
    public void handleRequest(final HttpServletRequest request,
                 final HttpServletResponse response)
        throws ServletException, IOException {
        if (request.getMethod().toUpperCase().equals("POST")) {
            String srcCity = request.getParameter("srcCity");
            String destCity = request.getParameter("destCity");
            double distance = cityService.findDistance(srcCity, destCity);
            request.setAttribute("distance", distance);
        }
        forward(request, response);
    }

    private void forward(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        RequestDispatcher dispatcher = request.getRequestDispatcher
Servlets
("WEB-INF/jsp/distance.jsp"); dispatcher.forward(request, response); } }

Now, we must wire the bean up in our applicationContext.xml file, like so:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="cityService"
        class="com.apress.springrecipes.city.CityServiceImpl">
        <property name="distanceMap">
            <map>
                <entry key="New York">
                    <map>
                        <entry key="London" value="5574" />
                        <entry key="Beijing" value="10976" />
                    </map>
                </entry>
            </map>
        </property>
    </bean>
<bean id="distance"
       class="com.apress.springrecipes.city.servlet.DistanceHttpRequestHandler">
        <property name="cityService" ref="cityService"/>
    </bean>

</beans>

Here, we've configured our bean and injected a reference to the CityServiceImpl instance. Note the bean id, distance, as it will be used to configure the servlet in the web.xml file.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>distance</servlet-name>
        <servlet-class>
            org.springframework.web.context.support.HttpRequestHandlerServlet
        </servlet-class>
    </servlet>

     <servlet-mapping>
        <servlet-name>distance</servlet-name>
        <url-pattern>/distance</url-pattern>
    </servlet-mapping>
</web-app>c

The use of the bean is identical as in the previous application—all code referencing the /distance endpoint will continue to work from the perspective of a bean. Try it yourself by launching your browser and pointing it to http://localhost:8080/distance?srcCity=New%20York&destCity=London.

Filters

The Spring framework provides a similar feature for filters. We will demonstrate a suitably simple filter configuration that simply iterates through the inbound request's request attributes and lists them. Here, we will use a collaborating object, of type CityServiceRequestAuditor, whose function it is to enumerate the request parameters (conceivably, such a filter could be used to send the data to syslog, to a monitoring agent like Splunk" or through JMX). The source code for CityServiceRequestAuditor is as follows:

package com.apress.springrecipes.city;

import java.util.Map;

public class CityServiceRequestAuditor {
    public void log(Map<String, String> attributes) {
        for (String k : attributes.keySet()) {
            System.out.println(String.format("%s=%s", k, attributes.get(k)));
        }
    }

In the servlet example, the HttpRequestHandlerServlet delegated to another object that implemented an interface—HttpRequestHandler—that was considerably simpler than that of a raw servlet. In the javax.servlet.Filter case, however, there is very little that can be done to simplify the interface, so we will instead delegate to an implementation of filter that's been configured using Spring.

Our filter implementation is as follows:

package com.apress.springrecipes.city.filter;

import com.apress.springrecipes.city.CityServiceRequestAuditor;

import javax.servlet.*;
import java.io.IOException;
import java.util.Map;


/**
 * This class is designed to intercept requests to the {@link
 com.apress.springrecipes.city.CityServiceImpl} and log them
 */
public class CityServiceRequestFilter implements Filter {
    private CityServiceRequestAuditor cityServiceRequestAuditor;

    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(final ServletRequest scervletRequest, final 
Filters
ServletResponse servletResponse, final FilterChain filterChain) throws IOException, ServletException { Map parameterMap = servletRequest.getParameterMap(); this.cityServiceRequestAuditor.log(parameterMap); filterChain.doFilter(servletRequest, servletResponse); }
@Override
    public void destroy() {
    }

    public void setCityServiceRequestAuditor(final CityServiceRequestAuditor
Filters
cityServiceRequestAuditor) { this.cityServiceRequestAuditor = cityServiceRequestAuditor; } }

It has a dependency on a bean of type CityServiceRequestAuditor. We will inject it and configure this bean in our Spring application context, below the previous configuration.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

   ...

    <bean id="cityServiceRequestAuditor"
            class="com.apress.springrecipes.city.CityServiceRequestAuditor" />

    <bean id="cityServiceRequestFilter"
Filters
class="com.apress.springrecipes.city.filter.CityServiceRequestFilter"> <property name="cityServiceRequestAuditor" ref="cityServiceRequestAuditor" /> </bean> </beans>

Now, all that remains is to setup the Spring org.springframework.web.filter.DelegatingFilterProxy instance in web.xml. This configuration maps the filter to the distance servlet we configured earlier.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

   ...

   <filter>
        <filter-name>cityServiceRequestFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
<filter-mapping>
        <filter-name>cityServiceRequestFilter</filter-name>
        <servlet-name>distance</servlet-name>
    </filter-mapping>

</web-app>

Again, note the use of engineered coincidence; the filter-name attribute is used to determine which bean in the root Spring application context to look up and delegate to. You might notice a bit of redundancy here: the filter interface exposes two methods for life cycle management—init and destroy—and the Spring context also provides life cycle management for your beans. The default behavior of the DelegatingFilterProxy is to not delegate those life cycle methods to your bean, preferring instead to let the Spring life cycle machinery (InitializingBean, @PostConstruct, etc. for initialization and DisposableBean, @PreDestroy, etc. for destruction) work instead. If you'd like to have Spring invoke those life cycle methods for you, set the init-param targetFilterLifecycle to true on the filter in the web.xml file:

<filter>
        <filter-name>cityServiceRequestFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

Integrating Spring with Struts 1.x

Problem

You would like to access beans declared in the Spring IoC container in a web application developed with Apache Struts 1.x.

Solution

A Struts application is able to load Spring's application context by registering the servlet listener ContextLoaderListener and access it from the servlet context just like in a generic web application. However, Spring offers better Struts-specific solutions for accessing its application context.

First, Spring allows you to load an application context by registering a Struts plug-in in the Struts configuration file. This application context will automatically refer to the application context loaded by the servlet listener as its parent so that it can refer to beans declared in its parent application context.

Second, Spring provides the ActionSupport class, a subclass of the Action base class that has a convenient getWebApplicationContext() method for you to access Spring's application context.

Finally, your Struts actions can have Spring beans injected via dependency injection. The prerequisite is to declare them in Spring's application context and ask Struts to look them up from Spring.

How It Works

Now, let's implement your web application for finding city distances using Apache Struts. First, you create the following directory structure for your web application.

Note

For a web application developed with Struts 1.3, you need to have Struts on your CLASSPATH. Additionally, to use Spring's support for Struts, you need to add a dependency to your CLASSPATH. If you are using Maven, add the following dependencies to your Maven project:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-struts</artifactId>
  <version>${spring.version}</version>
  <exclusions>
   <exclusion>
    <groupId>struts</groupId>
    <artifactId>struts</artifactId>
   </exclusion>
  </exclusions>
 </dependency>

 <dependency>
  <groupId>org.apache.struts</groupId>
  <artifactId>struts-core</artifactId>
  <version>1.3.10</version>
 </dependency>

Note that we exclude the version of Struts that the spring-struts supports depends on and instead to include a newer version whose Maven coordinates differ with that defined by the Spring-Struts dependency.

city/
    WEB-INF/
        classes/
        lib/*-jar
        jsp/
            distance.jsp
        applicationContext.xml
        struts-config.xml
        web.xml

In the web deployment descriptor (i.e., web.xml) of a Struts application, you have to register the Struts servlet ActionServlet to handle web requests. You can map this servlet to the URL pattern *.do.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>
            org.apache.struts.action.ActionServlet
        </servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

Loading Spring's Application Context into a Struts Application

There are two ways of loading Spring's application context into a Struts application. The first is to register the servlet listener ContextLoaderListener in web.xml. This is the generic approach that we've taken thus far for loading Spring application contexts into a Struts application. This listener loads /WEB-INF/applicationContext.xml as Spring's bean configuration file by default, so you don't need to specify its location explicitly.

<web-app ...>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    ...
</web-app>

Another way is to register the Struts plug-in ContextLoaderPlugin in the Struts configuration file struts-config.xml. By default, this plug-in loads the bean configuration file using the name of the ActionServlet instance registered in web.xml with -servlet.xml as the suffix (action-servlet.xml in this case). If you would like to load another bean configuration file, you can specify its name in the contextConfigLocation property.

<!DOCTYPE struts-config PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
    "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    ...
    <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
        <set-property property="contextConfigLocation"
            value="/WEB-INF/applicationContext.xml" />
    </plug-in>
</struts-config>

If both configurations exist at the same time, the Spring application context loaded by the Struts plug-in will automatically refer to the application context loaded by the servlet listener as its parent. Typically, business services should be declared in the application context loaded by the servlet listener, while web-related components should be separated in another application context loaded by the Struts plug-in. So let's omit the Struts plug-in setting for now.

Accessing Spring's Application Context in Struts Actions

Struts can help you to bind HTML form field values to a form bean's properties when a form is submitted. First, create a form class that extends ActionForm and includes two properties for a source city and a destination city.

package com.apress.springrecipes.city.struts;

import org.apache.struts.action.ActionForm;

public class DistanceForm extends ActionForm {

    private String srcCity;
    private String destCity;

    // Getters and Setters
    ...
}

Then, you'll create a JSP file that has a form for users to input a source and a destination city. You should define this form and its fields using the tag library provided by Struts so that these fields can be bound to a form bean's properties automatically. You can name this JSP file distance.jsp and put it in the WEB-INF/jsp directory to prevent direct access to it.

<%@ taglib prefix="html" uri="http://struts.apache.org/tags-html" %>

<html>
<head>
<title>City Distance</title>
</head>

<body>
<html:form method="POST" action="/distance.do">
<table>
  <tr>
    <td>Source City</td>
    <td><html:text property="srcCity" /></td>
  </tr>
  <tr>
    <td>Destination City</td>
    <td><html:text property="destCity" /></td>
  </tr>
  <tr>
    <td>Distance</td>
    <td>${distance}</td>
  </tr>
  <tr>
    <td colspan="2"><input type="submit" value="Find" /></td>
  </tr>
</table>
</html:form>
</body>
</html>

In Struts, each web request is processed by an action that extends the Action class. Sometimes, it's necessary for your Struts actions to access Spring beans. You can access the application context loaded by the servlet listener ContextLoaderListener through the static method WebApplicationContextUtils.getRequiredWebApplicationContext().

However, there's a better way to access Spring's application context in a Struts action—by extending the ActionSupport class. This class is a subclass of Action that provides a convenient method, getWebApplicationContext(), for you to access Spring's application context. This method first attempts to return the application context loaded by ContextLoaderPlugin. If it doesn't exist, this method attempts to return its parent (i.e., the application context loaded by ContextLoaderListener).

package com.apress.springrecipes.city.struts;
...
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.springframework.web.struts.ActionSupport;
public class DistanceAction extends ActionSupport {

    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) {
        if (request.getMethod().equals("POST")) {
            DistanceForm distanceForm = (DistanceForm) form;
            String srcCity = distanceForm.getSrcCity();
            String destCity = distanceForm.getDestCity();

            CityService cityService =
                (CityService) getWebApplicationContext().getBean("cityService");
            double distance = cityService.findDistance(srcCity, destCity);
            request.setAttribute("distance", distance);
        }
        return mapping.findForward("success");
    }
}

In the Struts configuration file struts-config.xml, you declare the form beans as well as the actions and their mappings for your application.

<!DOCTYPE struts-config PUBLIC 
Accessing Spring's Application Context in Struts Actions
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
Accessing Spring's Application Context in Struts Actions
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
Accessing Spring's Application Context in Struts Actions
<struts-config> <form-beans> <form-bean name="distanceForm" type="com.apress.springrecipes.city.struts.DistanceForm" /> </form-beans> <action-mappings> <action path="/distance" type="com.apress.springrecipes.city.struts.DistanceAction" name="distanceForm" validate="false"> <forward name="success" path="/WEB-INF/jsp/distance.jsp" /> </action> </action-mappings> </struts-config>

Now, you can deploy this application to your web container and access it through the URL http://localhost:8080/city/distance.do.

Declaring Struts Actions in Spring's Bean Configuration File

In addition to looking up Spring beans in a Struts action actively via Spring's application context, you can apply the dependency injection pattern to inject Spring beans into your Struts action. In this case, your Struts action no longer needs to extend the ActionSupport class; it simply extends the Action class.

package com.apress.springrecipes.city.struts;
...
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class DistanceAction extends Action {

    private CityService cityService;

    public void setCityService(CityService cityService) {
        this.cityService = cityService;
    }

    public ActionForward execute(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) {
        if (request.getMethod().equals("POST")) {
            ...
            double distance = cityService.findDistance(srcCity, destCity);
            request.setAttribute("distance", distance);
        }
        return mapping.findForward("success");
    }
}

However, this action must be under Spring's management to have its dependencies injected. You can choose to declare it in either applicationContext.xml or another bean configuration file loaded by ContextLoaderPlugin. To better separate business services and web components, I recommend declaring it in action-servlet.xml in the root of WEB-INF, which will be loaded by ContextLoaderPlugin by default, as your ActionServlet instance has the name action.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean name="/distance"
        class="com.apress.springrecipes.city.struts.DistanceAction">
        <property name="cityService" ref="cityService" />
    </bean>
</beans>

This action's bean name must be identical to its action path in struts-config.xml. As a <bean> element's id attribute cannot contain the / character, you should use the name attribute instead. In this configuration file, you can refer to beans declared in its parent application context, which is loaded by ContextLoaderListener.

In struts-config.xml, you have to register ContextLoaderPlugin to load the preceding bean configuration file.

...
<struts-config>
    ...
    <action-mappings>
        <action path="/distance"
            name="distanceForm" validate="false">
            <forward name="success"
                path="/WEB-INF/jsp/distance.jsp" />
        </action>
    </action-mappings>

    <controller processorClass="org.springframework.web.struts.
Declaring Struts Actions in Spring's Bean Configuration File
DelegatingRequestProcessor" /> <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn" /> </struts-config>

Also, you have to register the Struts request processor DelegatingRequestProcessor to ask Struts to look up its actions from Spring's application context by matching the action path with the bean name. With this request processor registered, you no longer need to specify the type attribute for an action.

Sometimes, you may already have another request processor registered so that you can't register DelegatingRequestProcessor. In this case, you can specify DelegatingActionProxy as your action's type to achieve the same effect.

<action path="/distance"
    type="org.springframework.web.struts.DelegatingActionProxy"
    name="distanceForm" validate="false">
    <forward name="success"
        path="/WEB-INF/jsp/distance.jsp" />
</action>

Integrating Spring with JSF

Problem

You would like to access beans declared in the Spring IoC container in a web application developed with JSF.

Solution

A JSF application is able to access Spring's application context just like a generic web application (i.e., by registering the servlet listener ContextLoaderListener and accessing it from the servlet context). However, due to the similarity between Spring's and JSF's bean models, it's very easy to integrate them by registering the Spring-provided JSF variable resolver DelegatingVariableResolver (for JSF 1.1) or the SpringBeanFacesELResolver (for JSF 1.2 and greater), which can resolve JSF variables into Spring beans. Furthermore, you can even declare JSF managed beans in Spring's bean configuration file to centralize them with your Spring beans.

How It Works

Suppose you are going to implement your web application for finding city distances using JSF. First, you create the following directory structure for your web application.

Note

Before you start developing a web application using JSF, you need a JSF implementation library. You can use the JSF Reference Implementation (JSF-RI) or a third party implementation. If you are using Maven, add the following dependencies to your Maven project:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
 </dependency>

 <dependency>
  <groupId>javax.faces</groupId>
  <artifactId>jsf-api</artifactId>
  <version>1.2_13</version>
 </dependency>

 <dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
  <version>1.1.2</version>
 </dependency>

 <dependency>
  <artifactId>jsf-impl</artifactId>
  <groupId>javax.faces</groupId>
  <version>1.2_13</version>
  <scope>provided</scope>
 </dependency>
<dependency>
  <groupId>org.apache.myfaces.core</groupId>
  <artifactId>myfaces-api</artifactId>
  <version>1.2.8</version>
 </dependency>

 <dependency>
  <groupId>org.apache.myfaces.core</groupId>
  <artifactId>myfaces-impl</artifactId>
  <version>1.2.8</version>
 </dependency>
city/
    WEB-INF/
        classes/
        lib/-*.jar
        applicationContext.xml
        faces-config.xml
        web.xml
    distance.jsp

In the web deployment descriptor (i.e., web.xml) of a JSF application, you have to register the JSF servlet FacesServlet to handle web requests. You can map this servlet to the URL pattern *.faces. To load Spring's application context at startup, you also have to register the servlet listener ContextLoaderListener.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>
            org.apache.myfaces.webapp.StartupServletContextListener
        </listener-class>
    </listener>
<listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>faces</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>faces</servlet-name>
        <url-pattern>*.faces</url-pattern>
    </servlet-mapping>
</web-app>

The basic idea of JSF is to separate presentation logic from UIs by encapsulating it in one or more JSF managed beans. For your distance-finding function, you can create the following DistanceBean class for a JSF managed bean:

package com.apress.springrecipes.city.jsf;
...
public class DistanceBean {

    private String srcCity;
    private String destCity;
    private double distance;
    private CityService cityService;

    public String getSrcCity() {
        return srcCity;
    }

    public String getDestCity() {
        return destCity;
    }

    public double getDistance() {
        return distance;
    }

    public void setSrcCity(String srcCity) {
        this.srcCity = srcCity;
    }

    public void setDestCity(String destCity) {
        this.destCity = destCity;
    }
public void setCityService(CityService cityService) {
        this.cityService = cityService;
    }

    public void find() {
        distance = cityService.findDistance(srcCity, destCity);
    }
}

There are four properties defined in this bean. As your page has to show the srcCity, destCity, and distance properties, you define a getter method for each of them. Users can only input the srcCity and destCity properties, so they require a setter method as well. The back-end CityService bean is injected via a setter method. When the find() method is called on this bean, it will invoke the back-end service to find the distance between these two cities and then store it in the distance property for subsequent display.

Then, you create distance.jsp in the root of your web application context. You have to put it here because when FacesServlet receives a request, it will map this request to a JSP file with the same name. For example, if you request the URL /distance.faces, then FacesServlet will load /distance.jsp accordingly.

<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>

<html>
<head>
<title>City Distance</title>
</head>

<body>
<f:view>
  <h:form>
    <h:panelGrid columns="2">
      <h:outputLabel for="srcCity">Source City</h:outputLabel>
      <h:inputText id="srcCity" value="#{distanceBean.srcCity}" />
      <h:outputLabel for="destCity">Destination   City</h:outputLabel>
      <h:inputText id="destCity" value="#{distanceBean.destCity}" />
      <h:outputLabel>Distance</h:outputLabel>
      <h:outputText value="#{distanceBean.distance}" />
      <h:commandButton value="Find" action="#{distanceBean.find}" />
    </h:panelGrid>
  </h:form>
</f:view>
</body>
</html>

This JSP file contains an <h:form> component for users to input a source city and a destination city. These two fields are defined using two <h:inputText> components, whose values are bound to a JSF managed bean's properties. The distance result is defined using an <h:outputText> component because its value is read-only. Finally, you define an <h:commandButton> component whose action will be triggered on the server side when you click it.

Resolving Spring Beans in JSF

The JSF configuration file faces-config.xml, located in the root of WEB-INF, is where you configure your navigation rules and JSF managed beans. For this simple application with only one screen, there's no navigation rule to configure. You can simply configure the preceding DistanceBean here. Here is the configuration file we might use for JSF 1.1:

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
    version="1.2">

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

    <managed-bean>
        <managed-bean-name>distanceBean</managed-bean-name>
        <managed-bean-class>
            com.apress.springrecipes.city.jsf.DistanceBean
        </managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
        <managed-property>
            <property-name>cityService</property-name>
            <value>#{cityService}</value>
        </managed-property>
    </managed-bean>
</faces-config>

Here is the same file, configured to use the JSF 1.2–exclusive SpringBeanFacesELResolver:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xi="http://www.w3.org/2001/XInclude"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
              http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">

    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>

    <managed-bean>
        <managed-bean-name>distanceBean</managed-bean-name>
        <managed-bean-class>
            com.apress.springrecipes.city.jsf.DistanceBean
        </managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>
<managed-property>
            <property-name>cityService</property-name>
            <value>#{cityService}</value>
        </managed-property>
    </managed-bean>

</faces-config>

The scope of DistanceBean is request, which means a new bean instance will be created on each request. Note that by registering the variable resolver DelegatingVariableResolver (or the SpringBeanFacesELResolver), you can easily refer to a bean declared in Spring's application context as a JSF variable in the form of #{beanName}. This variable resolver will first attempt to resolve variables from the original JSF variable resolver. If a variable cannot be resolved, this variable resolver will look up Spring's application context for a bean with the same name.

Now, you can deploy this application to your web container and access it through the URL http://localhost:8080/city/distance.faces.

Declaring JSF Managed Beans in Spring's Bean Configuration File

By registering DelegatingVariableResolver, you can refer to beans declared in Spring from JSF managed beans. However, they are managed by two different containers: JSF's and Spring's. A better solution is to centralize them under the management of Spring's IoC container. Let's remove the managed bean declaration from the JSF configuration file and add the following Spring bean declaration in applicationContext.xml:

<bean id="distanceBean"
    class="com.apress.springrecipes.city.jsf.DistanceBean"
    scope="request">
    <property name="cityService" ref="cityService" />
</bean>

To enable the request bean scope in Spring's application context, you have to register RequestContextListener in the web deployment descriptor.

<web-app ...>
    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <listener>
        <listener-class>
            org.springframework.web.context.request.RequestContextListener
        </listener-class>
    </listener>
    ...
</web-app>

Integrating Spring with DWR

Problem

You would like to access beans declared in the Spring IoC container in a web application developed with DWR.

Solution

DWR supports Spring by allowing you to expose Spring beans for remote invocation via its spring creator. Moreover, DWR 2.0 offers an XML schema for Spring that enables you to configure DWR inside Spring's bean configuration file. You can simply configure which beans to expose for remote invocation by embedding the <dwr:remote> tag without involving the DWR configuration file.

How It Works

Suppose you are going to use DWR to implement your web application for finding city distances with Ajax enabled. First, you create the following directory structure for your web application.

Note

To develop a web application using DWR, you have to have DWR in your WEB-INF/lib folder. If you are using Maven, add the following dependencies to your Maven project:

<dependency>
  <groupId>org.directwebremoting</groupId>
  <artifactId>dwr</artifactId>
  <version>2.0.3</version>
 </dependency>
city/
    WEB-INF/
        classes/
        lib/-*.jar
        applicationContext.xml
        dwr.xml
        web.xml
    distance.html

In the web deployment descriptor (i.e., web.xml) of a DWR application, you have to register the DWR servlet DwrServlet to handle Ajax web requests. You can map this servlet to the URL pattern /dwr/*. To load Spring's application context at startup, you also have to register the servlet listener ContextLoaderListener.

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
        http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <listener>
        <listener-class>
            org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>dwr</servlet-name>
        <servlet-class>
            org.directwebremoting.servlet.DwrServlet
        </servlet-class>
    </servlet>

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

A DWR application requires a configuration file to define which objects to expose for remote invocation by JavaScript. By default, DwrServlet loads dwr.xml from the root of WEB-INF as its configuration file.

<!DOCTYPE dwr PUBLIC
    "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN"
    "http://getahead.org/dwr/dwr20.dtd">

<dwr>
    <allow>
        <create creator="new" javascript="CityService">
            <param name="class"
                value="com.apress.springrecipes.city.CityServiceImpl" />
            <include method="findDistance" />
        </create>
    </allow>
</dwr>

This DWR configuration file exposes the CityServiceImpl class for remote invocation by JavaScript. The source of this class will be generated dynamically in CityService.js. The new creator is the most common DWR creator that will create a new instance of this class each time it's invoked. You only allow the findDistance() method of this class to be invoked remotely.

Exposing Spring Beans for Remote Invocation

DWR's new creator creates a new object instance each time it's invoked. If you would like to expose a bean in Spring's application context for remote invocation, you can make use of the spring creator and specify the name of the bean to expose.

<dwr>
    <allow>
        <create creator="spring" javascript="CityService">
            <param name="beanName" value="cityService" />
            <include method="findDistance" />
        </create>
    </allow>
</dwr>

Now, you can write a web page for users to find distances between cities. When using Ajax, your web page doesn't need to be refreshed like a traditional web page. So, you can simply create it as a static HTML page (e.g., distance.html, located in the root of your web application context).

<html>
<head>
<title>City Distance</title>
<script src='dwr/interface/CityService.js'></script>
<script src='dwr/engine.js'></script>
<script src='dwr/util.js'></script>
<script type="text/javascript">
function find() {
    var srcCity = dwr.util.getValue("srcCity");
    var destCity = dwr.util.getValue("destCity");
    CityService.findDistance(srcCity, destCity, function(data) {
        dwr.util.setValue("distance", data);
    });
}
</script>
</head>

<body>
<form>
<table>
  <tr>
    <td>Source City</td>
    <td><input type="text" id="srcCity" /></td>
  </tr>
  <tr>
    <td>Destination City</td>
    <td><input type="text" id="destCity" /></td>
  </tr>
  <tr>
    <td>Distance</td>
    <td><span id="distance" /></td>
  </tr>
<tr>
    <td colspan="2"><input type="button" value="Find" onclick="find()" /></td>
  </tr>
</table>
</form>
</body>
</html>

When a user clicks the Find button, the JavaScript function find() will be called. It makes an Ajax request to the CityService.findDistance() method by passing the values in the source city and destination city fields. When the Ajax response arrives, it displays the distance result in the distance span. To make this function work, you have to include the JavaScript libraries generated dynamically by DWR.

Now, you can deploy this application to your web container and access it through the URL http://localhost:8080/city/distance.html.

Configuring DWR in Spring's Bean Configuration File

DWR 2.0 supports configuring itself directly in Spring's bean configuration file. Before this is possible, however, you have to replace the previously registered DwrServlet with DwrSpringServlet in the web deployment descriptor.

<web-app ...>
    ...
    <servlet>
        <servlet-name>dwr</servlet-name>
        <servlet-class>
            org.directwebremoting.spring.DwrSpringServlet
        </servlet-class>
    </servlet>
</web-app>

In Spring's bean configuration file applicationContext.xml, you can configure DWR with the XML elements defined in the DWR schema. First, you have to declare the <dwr:configuration> element to enable DWR in Spring. Then, for each bean that you would like to expose for remote invocation, you embed a <dwr:remote> element with the configuration information equivalent to that in dwr.xml.

<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.5.xsd
        http://www.directwebremoting.org/schema/spring-dwr
        http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd">

    <dwr:configuration />
<bean id="cityService"
        class="com.apress.springrecipes.city.CityServiceImpl">
        <dwr:remote javascript="CityService">
            <dwr:include method="findDistance" />
        </dwr:remote>
        ...
    </bean>
</beans>

Now, you can delete dwr.xml because all its configuration information has been ported to Spring's bean configuration file.

Summary

In this chapter, you have learned how to integrate Spring into web applications developed with Servlet/JSP and popular web application frameworks such as Struts, JSF, and DWR. The integration mainly focuses on accessing beans declared in the Spring IoC container within these frameworks.

In a generic web application, regardless of which framework it uses, you can register the Spring-provided servlet listener ContextLoaderListener to load Spring's application context into the servlet context of this web application. Later, a servlet or any object that can access the servlet context will also be able to access Spring's application context through a utility method.

In a web application developed with Struts, you can load Spring's application context by registering a Struts plug-in in addition to the servlet listener. This application context will automatically refer to the application context loaded by the servlet listener as its parent. Spring provides the ActionSupport class that has a convenient method for you to access Spring's application context. You can also declare Struts actions in Spring's application context to have Spring beans injected.

For JSF, you can register the variable resolver DelegatingVariableResolver to resolve JSF variables into Spring beans. Furthermore, you can even declare JSF managed beans in Spring's bean configuration file to centralize them with Spring beans.

DWR allows you to expose Spring beans for remote invocation by its spring creator. DWR 2.0 offers an XML schema for Spring that enables you to configure DWR inside Spring's bean configuration file.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset