Thursday, January 13, 2011

Spring WS 2: Client-side WS-Security Using WSS4J

In this tutorial we will implement a client-side WS-Security using WSS4J. We will based our client application on an existing web service client which can be found at Spring WS - MVC: Implementing a Client Tutorial. It's imperative that you've read that first to understand the client application.

Our web service provider will be based from the Spring-WS 2: WS-Security Using WSS4J tutorial. We can also use the web service provider from the Spring-WS 2: WS-Security Using XWSS tutorial. This means you will be required to setup two servers with different ports, i.e two Tomcat instances (which is trivial to setup in Eclipse), one for the client and one for the provier. This also means you've read those articles first to understand what security tokens are required from the client.

We actually just need to edit a single file from our current web service client, the spring-ws.xml config. I'll show you the new config and the old config for comparison purposes.

Here's the new config:

spring-ws.xml (new)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:sws="http://www.springframework.org/schema/web-services"
       xmlns:oxm="http://www.springframework.org/schema/oxm"
          xsi:schemaLocation=
           "http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/web-services
           http://www.springframework.org/schema/web-services/web-services-2.0.xsd
           http://www.springframework.org/schema/oxm 
           http://www.springframework.org/schema/oxm/spring-oxm-1.5.xsd">

 <!--
   * The WebServiceTemplate requires a messageSender and messageFactory 
   * In order to facilitate the sending of plain Java objects, the WebServiceTemplate requires a marshaller and unmarshaller.
   * The WebServiceTemplate class uses an URI as the message destination. 
   See: http://static.springsource.org/spring-ws/sites/2.0/reference/html/client.html#client-web-service-template
 -->
 <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"
  p:marshaller-ref="jaxbMarshaller"
  p:unmarshaller-ref="jaxbMarshaller"
  p:defaultUri="http://localhost:8081/spring-ws-wss4j/krams/ws"
  p:messageSender-ref="messageSender">
  <constructor-arg ref="messageFactory"/> 
  <property name="interceptors">
   <list>
    <ref local="wss4jSecurityInterceptor"/>
   </list>
  </property>
 </bean>

 <!-- 
   References
    Chapter 7. Securing your Web services with Spring-WS
    - http://static.springsource.org/spring-ws/sites/2.0/reference/html/security.html
    7.3.  Wss4jSecurityInterceptor
    - http://static.springsource.org/spring-ws/sites/2.0/reference/html/security.html#security-wss4j-security-interceptor 
    Apache WSS4J
    - http://ws.apache.org/wss4j/
    Example of SOAP request authenticated with WS-UsernameToken
    - http://stackoverflow.com/questions/3448498/example-of-soap-request-authenticated-with-ws-usernametoken -->
 <!--  This is not documented in the Spring WS Reference but the API shows that we can add interceptors -->    
 <bean id="wss4jSecurityInterceptor"
  class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
  <!-- The web service provider requires us to pass a timestamp, 
    username, and password -->
  <property name="securementActions" value="Timestamp UsernameToken" />
  <property name="securementUsername" value="admin" />
  <property name="securementPassword" value="secret" />
  <!-- When the web service replies, it will send a timestamp,
    username, and password as well. We want to verify that it is still
    the same provider -->
  <property name="validationActions" value="Timestamp UsernameToken"/>
  <property name="validationCallbackHandler" ref="callbackHandler" />
 </bean>
 
 <!-- Simple callback handler that validates passwords against a in-memory Properties object. 
  Password validation is done on a case-sensitive basis -->
 <bean id="callbackHandler" class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler">
     <property name="users">
         <props>
             <prop key="mojo">mojopass</prop>
             <prop key="user">pass</prop>
         </props>
     </property>
 </bean>
 
 <!-- 
   There are two implementations of the WebServiceMessageSender:
   HttpUrlConnectionMessageSender and CommonsHttpMessageSender.
   
   The CommonsHttpMessageSender provides advanced and easy-to-use functionality 
   (such as authentication, HTTP connection pooling, and so forth).
   This uses the Jakarta Commons HttpClient. 
   See http://static.springsource.org/spring-ws/sites/2.0/reference/html/client.html#client-web-service-template
  -->
 <bean id="messageSender" class="org.springframework.ws.transport.http.CommonsHttpMessageSender"/>
 
 <!-- 
   There are two message factories for SOAP: SaajSoapMessageFactory and AxiomSoapMessageFactory. 
   If no message factory is specified (via the messageFactory property), Spring-WS will use 
   the SaajSoapMessageFactory by default.
   See: http://static.springsource.org/spring-ws/sites/2.0/reference/html/client.html#client-web-service-template
  -->
 <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
  
 <!-- Here we use the Jaxb2 marshaller to marshall and unmarshall our Java objects --> 
 <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"
  p:contextPath="org.krams.tutorial.oxm"/>
  
</beans>

Here's the old config:

spring-ws.xml (old)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p" 
    xmlns:sws="http://www.springframework.org/schema/web-services"
       xmlns:oxm="http://www.springframework.org/schema/oxm"
          xsi:schemaLocation=
           "http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/web-services
           http://www.springframework.org/schema/web-services/web-services-2.0.xsd
           http://www.springframework.org/schema/oxm 
           http://www.springframework.org/schema/oxm/spring-oxm-1.5.xsd">

 <!--
   * The WebServiceTemplate requires a messageSender and messageFactory 
   * In order to facilitate the sending of plain Java objects, the WebServiceTemplate requires a marshaller and unmarshaller.
   * The WebServiceTemplate class uses an URI as the message destination. 
   See: http://static.springsource.org/spring-ws/sites/2.0/reference/html/client.html#client-web-service-template
 -->
 <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"
  p:marshaller-ref="jaxbMarshaller"
  p:unmarshaller-ref="jaxbMarshaller"
  p:defaultUri="http://localhost:8081/spring-ws-standalone/krams/ws"
  p:messageSender-ref="messageSender">
  <constructor-arg ref="messageFactory"/> 
 </bean>

 <!-- 
   There are two implementations of the WebServiceMessageSender:
   HttpUrlConnectionMessageSender and CommonsHttpMessageSender.
   
   The CommonsHttpMessageSender provides advanced and easy-to-use functionality 
   (such as authentication, HTTP connection pooling, and so forth).
   This uses the Jakarta Commons HttpClient. 
   See http://static.springsource.org/spring-ws/sites/2.0/reference/html/client.html#client-web-service-template
  -->
 <bean id="messageSender" class="org.springframework.ws.transport.http.CommonsHttpMessageSender"/>
 
 <!-- 
   There are two message factories for SOAP: SaajSoapMessageFactory and AxiomSoapMessageFactory. 
   If no message factory is specified (via the messageFactory property), Spring-WS will use 
   the SaajSoapMessageFactory by default.
   See: http://static.springsource.org/spring-ws/sites/2.0/reference/html/client.html#client-web-service-template
  -->
 <bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
  
 <!-- Here we use the Jaxb2 marshaller to marshall and unmarshall our Java objects --> 
 <bean id="jaxbMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller"
  p:contextPath="org.krams.tutorial.oxm"/>
  
</beans>

Notice we still have the same basic bean declaration:
webServiceTemplate
messageSender
messageFactory
jaxbMarshaller

However the webServiceTemplate bean now has a reference to the interceptors property:
<property name="interceptors">
   <list>
    <ref local="wss4jSecurityInterceptor"/>
   </list>
  </property>
This is the property that's responsible for all the client-side WS-Security using WSS4J. It's really a simple addition to our existing web service client.

We also have to modify the defaultUri to match our current web service provider:
http://localhost:8081/spring-ws-wss4j/krams/ws
You may need to modify this property depending on how you setup your provider.

The Client-side WSS4J WS-Security
Let's examine further the wss4jSecurityInterceptor bean. Notice it contains a couple of properties. If you have read the Spring-WS 2: WS-Security Using WSS4J, you will find the following settings familiar because we're using the same exact interceptor!

<bean id="wss4jSecurityInterceptor"
  class="org.springframework.ws.soap.security.wss4j.Wss4jSecurityInterceptor">
  <!-- The web service provider requires us to pass a timestamp, 
    username, and password -->
  <property name="securementActions" value="Timestamp UsernameToken" />
  <property name="securementUsername" value="admin" />
  <property name="securementPassword" value="secret" />
  <!-- When the web service replies, it will send a timestamp,
    username, and password as well. We want to verify that it is still
    the same provider -->
  <property name="validationActions" value="Timestamp UsernameToken"/>
  <property name="validationCallbackHandler" ref="callbackHandler" />
 </bean>
 
 <!-- Simple callback handler that validates passwords against a in-memory Properties object. 
  Password validation is done on a case-sensitive basis -->
 <bean id="callbackHandler" class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler">
     <property name="users">
         <props>
             <prop key="mojo">mojopass</prop>
             <prop key="user">pass</prop>
         </props>
     </property>
 </bean>
The configuration defines policies pertaining to client-side WS-Security. It contains two parts:

1. The elements that the client must provide when sending a request message.
  • securementActions: When the client sends a message, a Timestamp and UsernameToken elements (including the username and password) will be added in the SOAP Header section.
  • securementUsername: The username that the provider expects
  • securementPassword: The password that the provider expects

2. The elements that the provider must provide when sending a response message.
  • validationActions: When the provider sends a reply, it should contain the actions we specified in the validationActions. If it doesn't contain the actions, then the reply is invalid. For our current configuration, we expect a Timestamp and UsernameToken as well from the provider. This is like a dual verification on both sides. The provider requires a Timestamp and UsernameToken. The client requires the same
  • validationCallbackHandler: When the provider sends a reply, it adds a username and password attributes. To validate these values in the client-side, we provide a callback handler, a SimplePasswordValidationCallbackHandler (see below) . In our current configuration, the client expects that the provider will send either of the following username/password pair:
    mojo/mojopass
    user/pass
<bean id="wss4jSecurityInterceptor"
 <!-- Simple callback handler that validates passwords against a in-memory Properties object. 
  Password validation is done on a case-sensitive basis -->
 <bean id="callbackHandler" class="org.springframework.ws.soap.security.wss4j.callback.SimplePasswordValidationCallbackHandler">
     <property name="users">
         <props>
             <prop key="mojo">mojopass</prop>
             <prop key="user">pass</prop>
         </props>
     </property>
 </bean>

Run the Client Application
Let's run our client application and examine the actual SOAP request message sent by the client:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
 <SOAP-ENV:Header>
  <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" SOAP-ENV:mustUnderstand="1">
   <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-2" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <wsse:Username>admin</wsse:Username>
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">1pN5RLpgXRhZxlYwHovlstf75do=</wsse:Password>
    <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">TOvp53qb3mPvX4YMi+Ac8g==</wsse:Nonce>
    <wsu:Created>2011-01-13T14:17:59.486Z</wsu:Created>
   </wsse:UsernameToken>
   <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-1">
    <wsu:Created>2011-01-13T14:17:59.473Z</wsu:Created>
    <wsu:Expires>2011-01-13T14:22:59.473Z</wsu:Expires>
   </wsu:Timestamp>
  </wsse:Security>
 </SOAP-ENV:Header>
 <SOAP-ENV:Body>
  <ns2:subscriptionRequest xmlns:ns2="http://krams915.blogspot.com/ws/schema/oss">
   <ns2:id>1234567</ns2:id>
   <ns2:name>John Smith</ns2:name>
   <ns2:email>john@dummy.com</ns2:email>
  </ns2:subscriptionRequest>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Notice the SOAP Header has been augmented with a Timestamp and UsernameToken including the username and password: admin/(encrypted password). A Nonce and Created date has been added as well.

Run the Provider Application
Let's run our web service provider and examine the actual SOAP reply message sent by the provider:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
 <SOAP-ENV:Header>
  <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" SOAP-ENV:mustUnderstand="1">
   <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-3" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <wsse:Username>mojo</wsse:Username>
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">mojopass</wsse:Password>
    <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">wVExJqRYGv9tYq8YUGkFyw==</wsse:Nonce>
    <wsu:Created>2011-01-13T14:24:17.026Z</wsu:Created>
   </wsse:UsernameToken>
   <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-2">
    <wsu:Created>2011-01-13T14:24:17.018Z</wsu:Created>
    <wsu:Expires>2011-01-13T14:29:17.018Z</wsu:Expires>
   </wsu:Timestamp><wsse11:SignatureConfirmation xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="SigConf-1"/>
  </wsse:Security>
 </SOAP-ENV:Header>
 <SOAP-ENV:Body>
  <subscriptionResponse xmlns="http://krams915.blogspot.com/ws/schema/oss">
   <code>SUCCESS</code>
   <description>User has been subscribed</description>
  </subscriptionResponse>
 </SOAP-ENV:Body>
</SOAP-ENV:Envelope>"

Notice the SOAP Header has been augmented with a Timestamp and UsernameToken including the username and password: mojo/mojopass. A Nonce and Created date has been added as well. These properties are exactly the same in both the provider and the client.

However, do not be mistaken. Our client application can also access XWSS-based web service providers! Just make sure you've set the correct URI. If you like to test this, feel free to run the provider application from Spring-WS 2: WS-Security Using XWSS tutorial.

That's it. We've completed our client-side WS-Security application using WSS4J. It's amazing how we enabled security on the client side. We just added a simple Wss4jSecurityInterceptor (the same bean we used in the provider application) and everything is running smoothly.

To run the client application, use the following URL:
http://localhost:8080/spring-ws-client-wss4j/krams/main/subscribe

Here's a screenshot of the client application:

The best way to learn further is to try the actual application

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-ws-2-0-0-rc2-tutorial/

You can download the project as a Maven build. Look for the spring-ws-client-wss4j.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring WS 2: Client-side WS-Security Using WSS4J ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring MVC 3 and jQuery Integration Tutorial

In this tutorial we will build a simple Spring MVC 3 application with AJAX capabilities using jQuery. We will explore how to post data using jQuery.post() and process the result. We will be developing a non-AJAX application first then convert it to an AJAX-powered version later.

What is jQuery?
jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is designed to change the way that you write JavaScript.

Source: http://jquery.com/

What is AJAX?
Ajax is a group of interrelated web development methods used on the client-side to create interactive web applications. With Ajax, web applications can retrieve data from the server asynchronously in the background without interfering with the display and behavior of the existing page. Data is usually retrieved using the XMLHttpRequest object. Despite the name, the use of XML is not needed, and the requests need not be asynchronous.

Like DHTML and LAMP, Ajax is not one technology, but a group of technologies. Ajax uses a combination of HTML and CSS to mark up and style information. The DOM is accessed with JavaScript to dynamically display, and to allow the user to interact with the information presented. JavaScript and the XMLHttpRequest object provide a method for exchanging data asynchronously between browser and server to avoid full page reloads.

Source: http://en.wikipedia.org/wiki/Ajax_(programming)

Our application is a simple arithmetic operation that adds two numbers and displays the sum. Here's a screenshot of the non-AJAX version:


Here's a screenshot of the AJAX version:


Notice nothing much is different, except that the non-AJAX version will display the result on another page, while the AJAX version will display on the same page. Actually, this is the main difference! With AJAX we have a responsive, desktop-like application. No page refresh.

Non-AJAX Version

Let's develop first our non-AJAX Spring MVC application.

We need a controller to handle the user's requests. Let's call it NonAjaxController

NonAjaxController
package org.krams.tutorial.controller;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.krams.tutorial.service.ArithmeticService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * Handles and retrieves the main requests
 */
@Controller
@RequestMapping("/main/nonajax")
public class NonAjaxController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="springService")
 private ArithmeticService springService;
 
    /**
     * Handles and retrieves the non-AJAX, ordinary Add page
     */
    @RequestMapping(value="/add", method = RequestMethod.GET)
    public String getNonAjaxAddPage() {
     logger.debug("Received request to show non-AJAX, ordinary add page");
    
     // This will resolve to /WEB-INF/jsp/nonajax-add-page.jsp
     return "nonajax-add-page";
 }
    
    /**
     * Handles request for adding two numbers
     */
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String add(@RequestParam(value="inputNumber1", required=true) Integer inputNumber1,
           @RequestParam(value="inputNumber2", required=true) Integer inputNumber2,
           Model model) {
  logger.debug("Received request to add two numbers");
  
  // Delegate to service to do the actual adding
  Integer sum = springService.add(inputNumber1, inputNumber2);
  
  // Add to model
  model.addAttribute("sum", sum);
  
     // This will resolve to /WEB-INF/jsp/nonajax-add-result-page.jsp
  return "nonajax-add-result-page";
 }
    
}
This controller declares the following mappings:
/main/nonajax/add (GET) - retrieves the add page
/main/nonajax/add POST) - computes the sum and retrieves the result page
The first mapping receives a request to display the add page. The second mapping receives two numbers and delegates the computation to the ArithmeticService. When the ArithmeticService is done processing, the controller then forwards the result to another JSP page which displays the result.

Here are the JSP pages:

nonajax-add-page.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 
 <title>Spring MVC - jQuery Integration Tutorial</title>
</head>
<body>

<h3>Spring MVC - jQuery Integration Tutorial</h3>
<h4>Non-AJAX version</h4>

<c:url var="addUrl" value="/krams/main/nonajax/add" />
<form method="POST" action="${addUrl}">

Demo 1 
<div style="border: 1px solid #ccc; width: 250px;">
 Add Two Numbers: <br/>
 <input id="inputNumber1" name="inputNumber1" type="text" size="5"> +
 <input id="inputNumber2" name="inputNumber2" type="text" size="5">
 <input type="submit" value="Add" /> <br/>
 Sum: (Result will be shown on another page)
</div>

</form>

</body>
</html>

nonajax-add-result-page.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 
 <title>Spring MVC - jQuery Integration Tutorial</title>
</head>
<body>

<h3>Spring MVC - jQuery Integration Tutorial</h3>
<h4>Non-AJAX version</h4>

Demo 1 Result
<div style="border: 1px solid #ccc; width: 250px;">
 Sum: ${sum}
</div>

</body>
</html>

Let's run the application. We'll be adding two numbers: 5 and 10, and we expect 10 as the result.

To access the Add page, enter the following URL in your browser:
http://localhost:8080/spring-mvc-jquery/krams/main/nonajax/add


Here's the result:

Now let's examine the service that performs the actual processing:

ArithmeticService
package org.krams.tutorial.service;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Service enables the class to be used as a Spring service
 * @Transactional enables transaction support for this class
 */
@Service("springService")
@Transactional
public class ArithmeticService {
 
 protected static Logger logger = Logger.getLogger("service");
 
 /**
  * Adds two numbers
  */
 public Integer add(Integer operand1, Integer operand2) {
  logger.debug("Adding two numbers");
  // A simple arithmetic addition
  return operand1 + operand2;
 }
 
}
This is a very simple POJO service that contains a simple arithmetic function. To make this POJO available as a Spring service bean, we just add the @Service annotation.

AJAX Version

Let us now convert our non-AJAX application to an AJAX-powered version.

To create our AJAX application we will create one JSP page to handle both the request and result on the same page. This is the primary benefit of AJAX. We also need another controller to handle the page request.

Here is the controller.

AjaxController
package org.krams.tutorial.controller;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.krams.tutorial.service.ArithmeticService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Handles and retrieves the main requests
 */
@Controller
@RequestMapping("/main/ajax")
public class AjaxController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="springService")
 private ArithmeticService springService;
 
 /**
  * Handles and retrieves the AJAX Add page
  */
    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String getAjaxAddPage() {
     logger.debug("Received request to show AJAX, add page");
     
     // This will resolve to /WEB-INF/jsp/ajax-add-page.jsp
     return "ajax-add-page";
 }
 
    /**
     * Handles request for adding two numbers
     */
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public @ResponseBody Integer add(@RequestParam(value="inputNumber1", required=true) Integer inputNumber1,
           @RequestParam(value="inputNumber2", required=true) Integer inputNumber2,
           Model model) {
  logger.debug("Received request to add two numbers");
  
  // Delegate to service to do the actual adding
  Integer sum = springService.add(inputNumber1, inputNumber2);
  
  // @ResponseBody will automatically convert the returned value into JSON format
  // You must have Jackson in your classpath
  return sum;
 }
}
This controller declares two mappings:
/main/ajax/add (GET) - retrieves the add page
/main/ajax/add (POST) - processes the sum
Notice the POST version of the add() will return an Integer but it's also annotated with @ResponseBody. With this annotation added, Spring will automatically convert the returned data to an appropriate response. In our case, it will convert the Integer to a JSON format if you have the Jackson library in your classpath

What is @ResponseBody
The @ResponseBody annotation instructs Spring MVC to serialize .... Spring MVC automatically serializes to JSON because the client accepts that content type.

Underneath the covers, Spring MVC delegates to a HttpMessageConverter to perform the serialization. In this case, Spring MVC invokes a MappingJacksonHttpMessageConverter built on the Jackson JSON processor. This implementation is enabled automatically when you use the mvc:annotation-driven configuration element with Jackson present in your classpath.

Source: http://blog.springsource.com/2010/01/25/ajax-simplifications-in-spring-3-0/

Here is the JSP page.

ajax-add-page.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 
 <script type="text/javascript" src="/spring-mvc-jquery/resources/js/jquery/jquery-1.4.4.min.js"></script>
 <script type="text/javascript">
     var jq = jQuery.noConflict();
 </script>
 
 <title>Spring MVC - jQuery Integration Tutorial</title>

</head>
<body>

<h3>Spring MVC - jQuery Integration Tutorial</h3>
<h4>AJAX version</h4>

Demo 1
<div style="border: 1px solid #ccc; width: 250px;">
 Add Two Numbers: <br/>
 <input id="inputNumber1" name="inputNumber1" type="text" size="5"> +
 <input id="inputNumber2" name="inputNumber2" type="text" size="5">
 <input type="submit" value="Add" onclick="add()" /> <br/>
 Sum: <span id="sum">(Result will be shown here)</span>
</div>


<script type="text/javascript"> 

function add() {
 jq(function() {
  // Call a URL and pass two arguments
  // Also pass a call back function
  // See http://api.jquery.com/jQuery.post/
  // See http://api.jquery.com/jQuery.ajax/
  // You might find a warning in Firefox: Warning: Unexpected token in attribute selector: '!' 
  // See http://bugs.jquery.com/ticket/7535
  jq.post("/spring-mvc-jquery/krams/main/ajax/add",
     {  inputNumber1:  jq("#inputNumber1").val(),
        inputNumber2:  jq("#inputNumber2").val() },
      function(data){
       // data contains the result
       // Assign result to the sum id
       jq("#sum").replaceWith('<span id="sum">'+ data + '</span>');
     });
 });
}

</script>
</body>
</html>
Notice in the head section, we have included the jQuery library:
<script type="text/javascript" src="/spring-mvc-jquery/resources/js/jquery/jquery-1.4.4.min.js"></script>
<script type="text/javascript">
     var jq = jQuery.noConflict();
</script>
We've also added a jQuery.noConflict(). This basically allows us to use any variable to represent a jQuery call. By default it uses $ to call its function. By assigning jQuery.noConflict() to a variable, we can now use this new variable to call all our jQuery functions. For more info, see http://api.jquery.com/jQuery.noConflict/

Let's examine our JSP page. Notice it contains two parts: the HTML part that shows the input, and the JavaScript part that handles the AJAX.

HTML part
Demo 1
<div style="border: 1px solid #ccc; width: 250px;">
 Add Two Numbers: <br/>
 <input id="inputNumber1" type="text" size="5"> +
 <input id="inputNumber2" type="text" size="5">
 <input type="submit" value="Add" onclick="add()" /> <br/>
 Sum: <span id="sum">(Result will be shown here)</span>
</div>
This is a simple form that takes two numbers. When the Add button is clicked, the JavaScript add() function is called.

JavaScript part
<script type="text/javascript"> 

function add() {
 jq(function() {
  // Call a URL and pass two arguments
  // Also pass a call back function
  // See http://api.jquery.com/jQuery.post/
  // See http://api.jquery.com/jQuery.ajax/
  // You might find a warning in Firefox: Warning: Unexpected token in attribute selector: '!' 
  // See http://bugs.jquery.com/ticket/7535
  jq.post("/spring-mvc-jquery/krams/main/ajax/add",
     {  inputNumber1:  jq("#inputNumber1").val(),
        inputNumber2:  jq("#inputNumber2").val() },
      function(data){
       // data contains the result
       // Assign result to the sum id
       jq("#sum").replaceWith('<span id="sum">'+ data + '</span>');
     });
 });
}

</script>

The add() function is a wrapper to jQuery's post() function. (For more info about post(), see See http://api.jquery.com/jQuery.post/) It takes any number of arguments and a callback function to handle the result. Here we're including the values from two input text fields: inputNumber1 and inputNumber2. Then we use an inner function function(data){...} to handle the result.
function(data){
       // data contains the result
       // Assign result to the sum id
       jq("#sum").replaceWith('<span id="sum">'+ data + '</span>');
     });
Here the sum is the name of the HTML element we assigned earlier in the HTML part:
Sum: <span id="sum">(Result will be shown here)</span>

Let's run our application and check the result.

To access the Add page, enter the following URL in your browser:
http://localhost:8080/spring-mvc-jquery/krams/main/ajax/add

Here's the result:

The result is displayed on the same page. There's no need to create another page just to view the result.

We're almost done with our tutorial. Now we need to add the required XML configurations to enable Spring MVC.

Let's start with the web.xml

web.xml
<servlet>
  <servlet-name>spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <servlet-mapping>
  <servlet-name>spring</servlet-name>
  <url-pattern>/krams/*</url-pattern>
 </servlet-mapping>

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

In the web.xml we declared a servlet-name spring. By convention, we must declare a spring-servlet.xml as well.

spring-servlet.xml
<!-- Declare a view resolver -->
 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
      p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

By convention, we must declare an applicationContext.xml as well.

applicationContext.xml
<!-- Activates various annotations to be detected in bean classes -->
 <context:annotation-config />
 
 <!-- Scans the classpath for annotated components that will be auto-registered as Spring beans.
  For example @Controller and @Service. Make sure to set the correct base-package-->
 <context:component-scan base-package="org.krams.tutorial" />
 
 <!-- Configures the annotation-driven Spring MVC Controller programming model.
 Note that, with Spring 3.0, this tag works in Servlet MVC only!  -->
 <mvc:annotation-driven /> 

Conclusion

That's it. We've completed our application. We've managed to build a simple Spring MVC 3 application with AJAX capabilities using jQuery. We've also explored how we can return JSON responses using @ResponseBody and the Jackson library

The best way to learn further is to try the actual application.

Download the project
You can access the project site at Google's Project Hosting at http://code.google.com/p/spring-mvc-jquery/

You can download the project as a Maven build. Look for the spring-mvc-jquery.zip in the Download sections.

You can run the project directly using an embedded server via Maven.
For Tomcat: mvn tomcat:run
For Jetty: mvn jetty:run

If you want to learn more about Spring MVC and integration with other technologies, feel free to read my other tutorials in the Tutorials section.
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring MVC 3 and jQuery Integration Tutorial ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share