Saturday, January 8, 2011

Spring MVC 3, Hibernate Annotations, MySQL Integration Tutorial

In this tutorial we will build a simple Spring MVC 3 application for managing a list of persons. We will provide a simple CRUD system for viewing, adding, editing, and deleting of persons. For the persistence layer, we will use Hibernate 3 annotations and use MySQL as our database, though the application is flexible enough to utilize different databases. A prior knowledge of MVC, ORM, and SQL is assumed in this tutorial. This tutorial is similar with Spring 3 MVC - Hibernate 3: Using Annotations Integration Tutorial

Spring MVC 3 and Hibernate Tutorials Series
Spring - Hibernate: Many-To-One Association - Explicitly Specify Join Table, Cascade, and Fetch
Spring - Hibernate: One-To-Many Association - Explicitly Specify Join Table, Cascade, and Fetch
Spring - Hibernate: Many-To-One Association
Spring - Hibernate: One-To-Many Association
Spring MVC 3, Hibernate Annotations, MySQL Integration Tutorial
Spring MVC 3, Hibernate Annotations, HSQLDB Integration Tutorial

What is Hibernate?
Hibernate is an object-relational mapping (ORM) library for the Java language, providing a framework for mapping an object-oriented domain model to a traditional relational database. Hibernate solves object-relational impedance mismatch problems by replacing direct persistence-related database accesses with high-level object handling functions

Source: http://en.wikipedia.org/wiki/Hibernate_(Java)

What is MySQL?
The MySQL Database powers the most demanding Web, E-commerce and Online Transaction Processing (OLTP) applications. It is a fully integrated transaction-safe, ACID compliant database with full commit, rollback, crash recovery and row level locking capabilities. MySQL delivers the ease of use, scalability, and performance that has made MySQL the world's most popular open source database. Some of the world's most trafficked websites like Facebook, Google, ticketmaster, and eBay rely on MySQL for their business critical applications.

Source: http://www.mysql.com/products/enterprise/database/
Let's preview first the final structure of our project.

And here's how our application would look like:
It's a simple, crude CRUD system (pun intended).

We start by defining our domain object Person

Person

package org.krams.tutorial.domain;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * For a complete reference see 
 * 
 * Hibernate Annotations Communit Documentations
 */
@Entity
@Table(name = "PERSON")
public class Person implements Serializable {

 private static final long serialVersionUID = -5527566248002296042L;
 
 @Id
 @Column(name = "ID")
 @GeneratedValue
 private Integer id;
 
 @Column(name = "FIRST_NAME")
 private String firstName;
 
 @Column(name = "LAST_NAME")
 private String lastName;
 
 @Column(name = "MONEY")
 private Double money;

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public Double getMoney() {
  return money;
 }

 public void setMoney(Double money) {
  this.money = money;
 }
}
Person is a simple POJO containing four private fields:
id
firstName
lastName
money
Each of these fields have been annotated with @Column and assigned with corresponding names.
ID
FIRST_NAME
LAST_NAME
MONEY
These column names are database column names. You don't deal with them. Instead, Hibernate is the one responsible for managing your database. However, you are responsible for declaring the column names in the POJO. You don't declare them in your database. Remember your database doesn't exist yet.

The POJO has been annotated to map to a database table. If you look at the declaration of the Person class we see the annotation @Table and the name of the actual table:

@Entity
@Table(name = "PERSON")
public class Person implements Serializable
Notice the annotation @Entity before the @Table. This tells Hibernate that this POJO should be mapped to a database table.

Since we will manipulate a list of persons, let's declare a service that manipulates a list of Persons.

PersonService
package org.krams.tutorial.service;

import java.util.List;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.krams.tutorial.domain.Person;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Service for processing Persons
 * 
 */
@Service("personService")
@Transactional
public class PersonService {

 protected static Logger logger = Logger.getLogger("service");
 
 @Resource(name="sessionFactory")
 private SessionFactory sessionFactory;
 
 /**
  * Retrieves all persons
  * 
  * @return a list of persons
  */
 public List getAll() {
  logger.debug("Retrieving all persons");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Create a Hibernate query (HQL)
  Query query = session.createQuery("FROM  Person");
  
  // Retrieve all
  return  query.list();
 }
 
 /**
  * Retrieves a single person
  */
 public Person get( Integer id ) {
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing person first
  Person person = (Person) session.get(Person.class, id);
  
  return person;
 }
 /**
  * Adds a new person
  */
 public void add(Person person) {
  logger.debug("Adding new person");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Save
  session.save(person);
 }
 
 /**
  * Deletes an existing person
  * @param id the id of the existing person
  */
 public void delete(Integer id) {
  logger.debug("Deleting existing person");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing person first
  Person person = (Person) session.get(Person.class, id);
  
  // Delete 
  session.delete(person);
 }
 
 /**
  * Edits an existing person
  */
 public void edit(Person person) {
  logger.debug("Editing existing person");
  
  // Retrieve session from Hibernate
  Session session = sessionFactory.getCurrentSession();
  
  // Retrieve existing person via id
  Person existingPerson = (Person) session.get(Person.class, person.getId());
  
  // Assign updated values to this person
  existingPerson.setFirstName(person.getFirstName());
  existingPerson.setLastName(existingPerson.getLastName());
  existingPerson.setMoney(existingPerson.getMoney());

  // Save updates
  session.save(existingPerson);
 }
}
We've declared a simple CRUD system with the following methods:
getAll()
add()
delete()
edit()
In each method we retrieve the session:
Session session = sessionFactory.getCurrentSession();
This is similar to retrieving a connection from the database so that we can do our work. The Session object provides numerous methods for persisting objects. For this tutorial, we use the following Session methods:
session.createQuery()
session.save()
session.delete()
We're done with the domain and the service layer. Let's move to the Spring controller.

MainController
package org.krams.tutorial.controller;

import java.util.List;

import javax.annotation.Resource;

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


/**
 * Handles and retrieves person request
 */
@Controller
@RequestMapping("/main")
public class MainController {

 protected static Logger logger = Logger.getLogger("controller");
 
 @Resource(name="personService")
 private PersonService personService;
 
 /**
  * Handles and retrieves all persons and show it in a JSP page
  * 
  * @return the name of the JSP page
  */
    @RequestMapping(value = "/persons", method = RequestMethod.GET)
    public String getPersons(Model model) {
     
     logger.debug("Received request to show all persons");
     
     // Retrieve all persons by delegating the call to PersonService
     List persons = personService.getAll();
     
     // Attach persons to the Model
     model.addAttribute("persons", persons);
     
     // This will resolve to /WEB-INF/jsp/personspage.jsp
     return "personspage";
 }
    
    /**
     * Retrieves the add page
     * 
     * @return the name of the JSP page
     */
    @RequestMapping(value = "/persons/add", method = RequestMethod.GET)
    public String getAdd(Model model) {
     logger.debug("Received request to show add page");
    
     // Create new Person and add to model
     // This is the formBackingOBject
     model.addAttribute("personAttribute", new Person());

     // This will resolve to /WEB-INF/jsp/addpage.jsp
     return "addpage";
 }
 
    /**
     * Adds a new person by delegating the processing to PersonService.
     * Displays a confirmation JSP page
     * 
     * @return  the name of the JSP page
     */
    @RequestMapping(value = "/persons/add", method = RequestMethod.POST)
    public String add(@ModelAttribute("personAttribute") Person person) {
  logger.debug("Received request to add new person");
  
     // The "personAttribute" model has been passed to the controller from the JSP
     // We use the name "personAttribute" because the JSP uses that name
  
  // Call PersonService to do the actual adding
  personService.add(person);

     // This will resolve to /WEB-INF/jsp/addedpage.jsp
  return "addedpage";
 }
    
    /**
     * Deletes an existing person by delegating the processing to PersonService.
     * Displays a confirmation JSP page
     * 
     * @return  the name of the JSP page
     */
    @RequestMapping(value = "/persons/delete", method = RequestMethod.GET)
    public String delete(@RequestParam(value="id", required=true) Integer id, 
              Model model) {
   
  logger.debug("Received request to delete existing person");
  
  // Call PersonService to do the actual deleting
  personService.delete(id);
  
  // Add id reference to Model
  model.addAttribute("id", id);
     
     // This will resolve to /WEB-INF/jsp/deletedpage.jsp
  return "deletedpage";
 }
    
    /**
     * Retrieves the edit page
     * 
     * @return the name of the JSP page
     */
    @RequestMapping(value = "/persons/edit", method = RequestMethod.GET)
    public String getEdit(@RequestParam(value="id", required=true) Integer id,  
              Model model) {
     logger.debug("Received request to show edit page");
    
     // Retrieve existing Person and add to model
     // This is the formBackingOBject
     model.addAttribute("personAttribute", personService.get(id));
     
     // This will resolve to /WEB-INF/jsp/editpage.jsp
     return "editpage";
 }
    
    /**
     * Edits an existing person by delegating the processing to PersonService.
     * Displays a confirmation JSP page
     * 
     * @return  the name of the JSP page
     */
    @RequestMapping(value = "/persons/edit", method = RequestMethod.POST)
    public String saveEdit(@ModelAttribute("personAttribute") Person person, 
                 @RequestParam(value="id", required=true) Integer id, 
                Model model) {
     logger.debug("Received request to update person");
    
     // The "personAttribute" model has been passed to the controller from the JSP
     // We use the name "personAttribute" because the JSP uses that name
     
     // We manually assign the id because we disabled it in the JSP page
     // When a field is disabled it will not be included in the ModelAttribute
     person.setId(id);
     
     // Delegate to PersonService for editing
     personService.edit(person);
     
     // Add id reference to Model
  model.addAttribute("id", id);
  
     // This will resolve to /WEB-INF/jsp/editedpage.jsp
  return "editedpage";
 }
    
}
This controller declares the following mappings:
/persons - for retrieving all persons
/persons/add (GET) - displays the Add New form
/persons/add (POST) - saves the new person
/persons/delete - deletes an existing person
/persons/edit (GET) - displays the Edit form
/persons/edit (POST) - saves the edited person

Each mapping delegates the call to the PersonService. When the PersonService is done processing, the controller then forwards the request to a JSP page that displays a confirmation message. Here are the JSP pages.

personspage.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>Insert title here</title>
</head>
<body>
<h1>Persons</h1>

<c:url var="addUrl" value="/krams/main/persons/add" />
<table style="border: 1px solid; width: 500px; text-align:center">
 <thead style="background:#fcf">
  <tr>
   <th>First Name</th>
   <th>Last Name</th>
   <th>Money</th>
   <th colspan="3"></th>
  </tr>
 </thead>
 <tbody>
 <c:forEach items="${persons}" var="person">
   <c:url var="editUrl" value="/krams/main/persons/edit?id=${person.id}" />
   <c:url var="deleteUrl" value="/krams/main/persons/delete?id=${person.id}" />
  <tr>
   <td><c:out value="${person.firstName}" /></td>
   <td><c:out value="${person.lastName}" /></td>
   <td><c:out value="${person.money}" /></td>
   <td><a href="${editUrl}">Edit</a></td>
   <td><a href="${deleteUrl}">Delete</a></td>
   <td><a href="${addUrl}">Add</a></td>
  </tr>
 </c:forEach>
 </tbody>
</table>

<c:if test="${empty persons}">
 There are currently no persons in the list. <a href="${addUrl}">Add</a> a person.
</c:if>

</body>
</html>
editpage.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ 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>Insert title here</title>
</head>
<body>

<h1>Edit Person</h1>

<c:url var="saveUrl" value="/krams/main/persons/edit?id=${personAttribute.id}" />
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
 <table>
  <tr>
   <td><form:label path="id">Id:</form:label></td>
   <td><form:input path="id" disabled="true"/></td>
  </tr>
 
  <tr>
   <td><form:label path="firstName">First Name:</form:label></td>
   <td><form:input path="firstName"/></td>
  </tr>

  <tr>
   <td><form:label path="lastName">Last Name</form:label></td>
   <td><form:input path="lastName"/></td>
  </tr>
  
  <tr>
   <td><form:label path="money">Money</form:label></td>
   <td><form:input path="money"/></td>
  </tr>
 </table>
 
 <input type="submit" value="Save" />
</form:form>

</body>
</html>
addpage.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ 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>Insert title here</title>
</head>
<body>

<h1>Create New Person</h1>

<c:url var="saveUrl" value="/krams/main/persons/add" />
<form:form modelAttribute="personAttribute" method="POST" action="${saveUrl}">
 <table>
  <tr>
   <td><form:label path="firstName">First Name:</form:label></td>
   <td><form:input path="firstName"/></td>
  </tr>

  <tr>
   <td><form:label path="lastName">Last Name</form:label></td>
   <td><form:input path="lastName"/></td>
  </tr>
  
  <tr>
   <td><form:label path="money">Money</form:label></td>
   <td><form:input path="money"/></td>
  </tr>
 </table>
 
 <input type="submit" value="Save" />
</form:form>

</body>
</html>
editedpage.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="java.util.Date" %>
<%@ 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>Insert title here</title>
</head>
<body>

<h1>Persons</h1>

<p>You have edited a person with id ${id} at <%= new java.util.Date() %></p>

<c:url var="mainUrl" value="/krams/main/persons" />
<p>Return to <a href="${mainUrl}">Main List</a></p>

</body>
</html>
addedpage.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="java.util.Date" %>
<%@ 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>Insert title here</title>
</head>
<body>

<h1>Persons</h1>

<p>You have added a new person at <%= new java.util.Date() %></p>

<c:url var="mainUrl" value="/krams/main/persons" />
<p>Return to <a href="${mainUrl}">Main List</a></p>

</body>
</html>
deletedpage.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page import="java.util.Date" %>
<%@ 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>Insert title here</title>
</head>
<body>

<h1>Persons</h1>

<p>You have deleted a person with id ${id} at <%= new java.util.Date() %></p>

<c:url var="mainUrl" value="/krams/main/persons" />
<p>Return to <a href="${mainUrl}">Main List</a></p>

</body>
</html>

Let's complete our Spring MVC application by declaring the required configurations.

To enable Spring MVC we need to add in 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>
Take note of the URL pattern. When accessing any pages in our MVC application, the host name must be appended with
/krams
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 /> 
 
 <!-- Load Hibernate related configuration -->
 <import resource="hibernate-context.xml" />
Notice in the applicationContext.xml, we declared the following import:
<import resource="hibernate-context.xml" />
This contains the Hibernate configuration files

<?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:tx="http://www.springframework.org/schema/tx"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context
   http://www.springframework.org/schema/context/spring-context-3.0.xsd
      ">

 <context:property-placeholder location="/WEB-INF/spring.properties" />
    
    <!-- Enable annotation style of managing transactions -->
 <tx:annotation-driven transaction-manager="transactionManager" /> 
   
    <!-- Declare the Hibernate SessionFactory for retrieving Hibernate sessions -->
    <!-- See http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.html -->        
 <!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/SessionFactory.html -->
 <!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/Session.html -->
 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
     p:dataSource-ref="dataSource"
     p:configLocation="${hibernate.config}"
     p:packagesToScan="org.krams.tutorial"/>
 
 <!-- Declare a datasource that has pooling capabilities-->  
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close"
    p:driverClass="${app.jdbc.driverClassName}"
    p:jdbcUrl="${app.jdbc.url}"
    p:user="${app.jdbc.username}"
    p:password="${app.jdbc.password}"
    p:acquireIncrement="5"
    p:idleConnectionTestPeriod="60"
    p:maxPoolSize="100"
    p:maxStatements="50"
    p:minPoolSize="10" />

 <!-- Declare a transaction manager-->
 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager" 
          p:sessionFactory-ref="sessionFactory" />
  
</beans>
We basically encapsulated all Hibernate and Spring related configurations in this one XML file. Here's what happening within the config:

1. Enable transaction support through Spring annotations:
<tx:annotation-driven transaction-manager="transactionManager" />
2. Declare the Hibernate SessionFactory:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"
     p:dataSource-ref="dataSource"
     p:configLocation="${hibernate.config}"
     p:packagesToScan="org.krams.tutorial"/>
A SessionFactory is a factory that produces Session objects. It's analogous to a real Car Factory or Car Assemblies where its job is to make cars for humans.

What is a Session?
The main function of the Session is to offer create, read and delete operations for instances of mapped entity classes.

Source: http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/Session.html

A SessionFactory requires a datasource. The datasource in this tutorial is the database.

A SessionFactory requires a configLocation. The configLocation contains Hibernate-specific configurations. Here's our Hibernate-specific config file:

hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
  
<hibernate-configuration>
  <session-factory>
   <!-- We're using MySQL database so the dialect needs to MySQL as well-->
    <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
    <!-- Enable this to see the SQL statements in the logs-->
    <property name="show_sql">false</property>
    <!-- This will drop our existing database and re-create a new one.
      Existing data will be deleted! -->
    <property name="hbm2ddl.auto">create</property>
  </session-factory>
</hibernate-configuration>
Here we declared the type of database to be used. We are using MySQL so we use a MySQL dialect. We're using a specialized MySQL5InnoDBDialect because we decided to use the InnoDB storage engine for MySQL.

What's MySQL InnoDB?
InnoDB is a transaction-safe (ACID compliant) storage engine for MySQL that has commit, rollback, and crash-recovery capabilities to protect user data.

Source: http://dev.mysql.com/doc/refman/5.0/en/innodb-storage-engine.html
Returning back to the SessionFactory bean declaration, it also requires the property packagesToScan. This is to indicate where our annotated entities are located. In this tutorial, it's under the org.krams.tutorial

3. Declare a datasource:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
 destroy-method="close"
 p:driverClass="${app.jdbc.driverClassName}"
 p:jdbcUrl="${app.jdbc.url}"
 p:user="${app.jdbc.username}"
 p:password="${app.jdbc.password}"
 p:acquireIncrement="5"
 p:idleConnectionTestPeriod="60"
 p:maxPoolSize="100"
 p:maxStatements="50"
 p:minPoolSize="10" />
Our datasource uses C3P0 for pooling to allow efficient access to our database. Why do we need to wrap our datasource with a connection pool?
JDBC connections are often managed via a connection pool rather than obtained directly from the driver. Examples of connection pools include BoneCP, C3P0 and DBCP.

Source: http://en.wikipedia.org/wiki/Java_Database_Connectivity

What is Pooling?
In software engineering, a connection pool is a cache of database connections maintained so that the connections can be reused when future requests to the database are required. Connection pools are used to enhance the performance of executing commands on a database. Opening and maintaining a database connection for each user, especially requests made to a dynamic database-driven website application, is costly and wastes resources. In connection pooling, after a connection is created, it is placed in the pool and it is used over again so that a new connection does not have to be established.

Source: http://en.wikipedia.org/wiki/Connection_pool

For more info on configuring C3P0, you can check this reference from JBoss: HowTo configure the C3P0 connection pool. For a list of other pooling providers, see Open Source Database Connection Pools

The database-specific configuration are contained within a properties file.

spring.properties

mydatabase is the name of our MySQL dabatase.

As an alternative, we can enter these properties directly within the hibernate-context.xml

This is exactly similar to the following:


The benefit of using a separate properties file is we encapsulate all database-specific configs within a separate file. The hibernate-context.xml purpose is to encapsulate Hibernate-related config not database properties.

That's it. We've completed our application. We've managed to setup a simple Spring MVC 3 application that uses Hibernate Annotations to encapsulate persistence access to a MySQL database. We've also leveraged Spring's simple MVC programming model.

To access the main page, enter the following URL:
http://localhost:8080/spring-hibernate-mysql/krams/main/persons
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-hibernate-annotations-integration-tutorial/

You can download the project as a Maven build. Look for the spring-hibernate-mysql.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, Hibernate Annotations, MySQL Integration Tutorial ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring Security - MVC: Using an Embedded LDAP Server

In this tutorial we will setup a simple Spring MVC 3 application, secured by Spring Security. ur users will be authenticated against an LDAP provider. This tutorial is exactly similar with the Spring Security - MVC: Using an LDAP Authentication Provider tutorial. The main difference now is we will be using an embedded LDAP server for testing our application.

What is LDAP?
The Lightweight Directory Access Protocol (LDAP) is an application protocol for reading and editing directories over an IP network. A directory is an organized set of records. For example, the telephone directory is an alphabetical list of persons and organizations, with each record having an address and phone number. A directory information tree often follows political, geographic, or organizational boundaries. LDAP directories often use Domain Name System (DNS) names for the highest levels. Deeper inside the directory might appear entries for people, departments, teams, printers, and documents.

Source: http://en.wikipedia.org/wiki/LDAP

How do we embed an LDAP server using Spring Security?
18.3.1 Using an Embedded Test Server

The <ldap-server> element can also be used to create an embedded server, which can be very useful for testing and demonstrations. In this case you use it without the url attribute:

<ldap-server root="dc=springframework,dc=org"/>

Here we've specified that the root DIT of the directory should be “dc=springframework,dc=org”, which is the default. Used this way, the namespace parser will create an embedded Apache Directory server and scan the classpath for any LDIF files, which it will attempt to load into the server. You can customize this behaviour using the ldif attribute, which defines an LDIF resource to be loaded:

<ldap-server ldif="classpath:users.ldif" />

Source: Spring Security 3 Reference
To convert our previous application from using an actual LDAP server to an embedded server, all we need to do is modify the

Let's open the spring-security.xml file and modify the tag to the new configuration:

// Old configuration



// New configuration


Here's the updated spring-security.xml:

spring-security.xml
<?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:security="http://www.springframework.org/schema/security"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/security 
   http://www.springframework.org/schema/security/spring-security-3.0.xsd">
 
 <!-- This is where we configure Spring-Security  -->
 <security:http auto-config="true" use-expressions="true" access-denied-page="/krams/auth/denied" >
 
  <security:intercept-url pattern="/krams/auth/login" access="permitAll"/>
  <security:intercept-url pattern="/krams/main/admin" access="hasRole('ROLE_ADMIN')"/>
  <security:intercept-url pattern="/krams/main/common" access="hasRole('ROLE_USER')"/>
  
  <security:form-login
    login-page="/krams/auth/login" 
    authentication-failure-url="/krams/auth/login?error=true" 
    default-target-url="/krams/main/common"/>
   
  <security:logout 
    invalidate-session="true" 
    logout-success-url="/krams/auth/login" 
    logout-url="/krams/auth/logout"/>
 
 </security:http>
 
 <!-- 
   For authentication:
   user-search-filter: the attribute name that contains the user name 
      user-search-base: the base path where to find user information
      
      For authorization:
      group-search-filter: the attribute name that contains the full dn of a user
      group-search-base: the base path where to find role information
      group-role-attribute: the attribute name that contains the role type
      role-prefix: the prefix to be added when retrieving role values
      
      For server access:
      manager-dn: the full dn of the person that has access to an LDAP server
      manager-password: the password of the person that has access to an LDAP server
 -->
 <security:authentication-manager>
         <security:ldap-authentication-provider  
           user-search-filter="(uid={0})" 
           user-search-base="ou=users"
           group-search-filter="(uniqueMember={0})"
           group-search-base="ou=groups"
           group-role-attribute="cn"
           role-prefix="ROLE_">
         </security:ldap-authentication-provider>
 </security:authentication-manager>
 
 <!-- Use an embedded LDAP server. We need to declare the location of the LDIF file
   We also need to customize the root attribute default is "dc=springframework,dc=org"  -->
 <security:ldap-server ldif="classpath:mojo.ldif"  root="o=mojo"/>
 
</beans>
For the embedded server to work, we need to include LDIF file in the classpath:

mojo.ldif
version: 1

dn: o=mojo
objectClass: organization
objectClass: extensibleObject
objectClass: top
o: mojo

dn: ou=users,o=mojo
objectClass: extensibleObject
objectClass: organizationalUnit
objectClass: top
ou: users

dn: ou=groups,o=mojo
objectClass: extensibleObject
objectClass: organizationalUnit
objectClass: top
ou: groups

dn: cn=User,ou=groups,o=mojo
objectClass: groupOfUniqueNames
objectClass: top
cn: User
uniqueMember: cn=John Milton,ou=users,o=mojo
uniqueMember: cn=Robert Browning,ou=users,o=mojo
uniqueMember: cn=Hugo Williams,ou=users,o=mojo
uniqueMember: cn=John Keats,ou=users,o=mojo

dn: cn=Admin,ou=groups,o=mojo
objectClass: groupOfUniqueNames
objectClass: top
cn: Admin
uniqueMember: cn=Hugo Williams,ou=users,o=mojo
uniqueMember: cn=John Keats,ou=users,o=mojo

dn: cn=Robert Browning,ou=users,o=mojo
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
objectClass: top
cn: Robert Browning
sn: Browning
uid: rbrowning
userPassword:: cGFzcw==

dn: cn=John Keats,ou=users,o=mojo
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
objectClass: top
cn: John Keats
sn: Keats
uid: jkeats
userPassword:: cGFzcw==

dn: cn=Hugo Williams,ou=users,o=mojo
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
objectClass: top
cn: Hugo Williams
sn: Williams
uid: hwilliams
userPassword:: cGFzcw==

dn: cn=John Milton,ou=users,o=mojo
objectClass: organizationalPerson
objectClass: person
objectClass: inetOrgPerson
objectClass: top
cn: John Milton
sn: Milton
uid: jmilton
userPassword:: cGFzcw==
Then we have to modify our pom.xml to include the necessary dependencies for the embedded ApacheDS server. The depencies are big, so it might take a while to download.

pom.xml
    <dependency>
     <groupId>org.apache.directory.server</groupId>
     <artifactId>apacheds-all</artifactId>
     <version>1.5.5</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-api</artifactId>
     <version>1.5.6</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>org.slf4j</groupId>
     <artifactId>slf4j-log4j12</artifactId>
     <version>1.5.6</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>

To access the login page, enter the following URL:
http://localhost:8081/spring-security-ldap-embedded/krams/auth/login
To logout, enter the following URL:
http://localhost:8081/spring-security-ldap-embedded/krams/auth/logout
To access the admin page, enter the following URL:
http://localhost:8081/spring-security-ldap-embedded/krams/main/admin
To access the common page, enter the following URL:
http://localhost:8081/spring-security-ldap-embedded/krams/main/common
Here are the usernames and passwords:
rbrowning - pass
jkeats - pass
hwilliams - pass
jmilton - pass

That's it. We've managed to setup a simple Spring MVC 3 application, that's secured by Spring Security. We also used an embedded LDAP server for authenticating our users.

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-security-ldap/

You can download the project as a Maven build. Look for the spring-security-ldap-embedded.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
StumpleUpon DiggIt! Del.icio.us Blinklist Yahoo Furl Technorati Simpy Spurl Reddit Google I'm reading: Spring Security - MVC: Using an Embedded LDAP Server ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring and GWT: Security via Spring Security

In this tutorial we will secure our existing GWT application through Spring Security. We will create a transparent layer for managing our application's security. We will be using the application we built in the following tutorial: Spring and GWT Integration using Maven and GWTHandler (Part 1). Please read that first, so that you have a basic idea of the project's structure. Also, please read Spring Security 3 - MVC: Using a Simple User-Service Tutorial for a basic introduction in setting-up Spring Security.

What is Spring Security?
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications

Spring Security is one of the most mature and widely used Spring projects. Founded in 2003 and actively maintained by SpringSource since, today it is used to secure numerous demanding environments including government agencies, military applications and central banks.

Source: http://static.springsource.org/spring-security/site/index.html

What is GWT?
Google Web Toolkit (GWT) is a development toolkit for building and optimizing complex browser-based applications. GWT is used by many products at Google, including Google AdWords and Orkut. It's open source, completely free, and used by thousands of developers around the world.

Source: http://code.google.com/webtoolkit/

We start by editing the web.xml file to enable the Spring Security filters (We will not discuss again what we've covered in the previous tutorials).

web.xml





 
 
  contextConfigLocation    /WEB-INF/spring-security.xml
  /WEB-INF/applicationContext.xml
   
 
 
   
         springSecurityFilterChain
         org.springframework.web.filter.DelegatingFilterProxy
 
 
 
 
         springSecurityFilterChain
         /*
 

 
 
  org.springframework.web.context.ContextLoaderListener
 

 
 
  gwt
  org.springframework.web.servlet.DispatcherServlet
  1
 
 
 
 
  spring
  org.springframework.web.servlet.DispatcherServlet
  1
 
 
 
 
  gwt
  gwtmodule/rpc/*
 
 
 
 
  spring
  krams/*
 

We have declared a reference to a spring-security.xml configuration. This contains the Spring Security settings for our application.

spring-security.xml


 
 
 
 
  
  
  
  
  
  
  
  
  
  
  
     
  
  
   
  
 
 
 
 
 
         
           
         
 
 
 
 

  
  
  
     
     
   
 
   
 
 
   
 
 
  
 
  
The defining configuration here are the custom beans we've created. Here's the reason: In a normal Spring MVC application with Spring Security enabled regardless whether the user is authorized or not, he will be redirected to a corresponding View, i.e a JSP page. However, GWT is an AJAX application that operates in the RPC level. If you try redirecting to a page, it will not work. The solution is to provide a custom HttpServletResponse response to tell GWT that the user is authorized or not. These responses are HTTP status codes:
200 - Authenticated and authorized
401 - Not authenticated and not authorized
401 - (Or) authenticated but not authorized

The developer's job is to interpret what to do next for these set of responses. Here are the custom beans.

CustomAuthenticationEntryPoint
/**
 * 
 */
package org.krams.tutorial.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

 protected static Logger logger = Logger.getLogger("security");
 
 public void commence(HttpServletRequest request, HttpServletResponse response,
   AuthenticationException exception) throws IOException, ServletException {

   logger.debug("Authentication required");
   
    HttpServletResponse httpResponse = (HttpServletResponse) response;
         httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication required");
 }

}
CustomAuthenticationSuccessHandler
package org.krams.tutorial.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class CustomAuthenticationSuccessHandler implements
  AuthenticationSuccessHandler {

 protected static Logger logger = Logger.getLogger("security");

 public void onAuthenticationSuccess(HttpServletRequest request,
   HttpServletResponse response, Authentication authentication) throws IOException,
   ServletException {
  
   logger.debug("Authentication success");
   
     HttpServletResponse httpResponse = (HttpServletResponse) response;
         httpResponse.sendError(HttpServletResponse.SC_OK, "Authentication success");
 }

}
CustomAuthenticationFailureHandler
package org.krams.tutorial.security;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

public class CustomAuthenticationFailureHandler implements
  AuthenticationFailureHandler {

 protected static Logger logger = Logger.getLogger("security");
 
 public void onAuthenticationFailure(HttpServletRequest request,
   HttpServletResponse response, AuthenticationException exception)
   throws IOException, ServletException {
  
   logger.debug("Authentication failure");
   
   HttpServletResponse httpResponse = (HttpServletResponse) response;
   httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failure");  
 }

}
Let's run our application and see what will happen. Again, we use the Maven gwt:run goal to execute the application.


After sending our name, we get a server error message. How do we know that this is caused by Spring Security and not by a missing RPC service? We check the logs!

Here's the Jetty log:
00:00:18.667 [WARN] 401 - POST /gwtmodule/rpc/greet (127.0.0.1) 1433 bytes

Here's the log4j log:
[DEBUG] [btpool0-0 04:34:46] (CustomAuthenticationEntryPoint.java:commence:23) Authentication required

Clearly it's because we're not authorized. At this point, we know our services are secured! However, the error message is misleading. We need to provide a friendly message.

Go the client package of our project. Open the gwtmodule.java, and replace the contents of the sendNametoServer() method with the following:

gwtmodule.java
  /**
       * Send the name from the nameField to the server and wait for a response.
       */
      private void sendNameToServer() {
        // First, we validate the input.
        errorLabel.setText("");
        final String textToServer = nameField.getText();
        if (!FieldVerifier.isValidName(textToServer)) {
          errorLabel.setText("Please enter at least four characters");
          return;
        }

        // Then, we send the input to the server.
        sendButton.setEnabled(false);
        textToServerLabel.setText(textToServer);
        serverResponseLabel.setText("");
        
        // We first check if user is authorized by checking a flag URL
        
        // Create a new request
        RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, "/krams/main/admin");
  rb.setCallback(new RequestCallback() {
   
      public void onError(Request request, Throwable exception) {
       // Error in sending request
      }
      
      public void onResponseReceived(Request request, Response response) {
          if (response.getStatusCode() == 200) {
           
           // Send the input to the server.
           greetingService.greetServer(textToServer, new AsyncCallback() {
                  public void onFailure(Throwable caught) {
                    // Show the RPC error message to the user
                    dialogBox.setText("Remote Procedure Call - Failure");
                    serverResponseLabel.addStyleName("serverResponseLabelError");
                    //serverResponseLabel.setHTML(SERVER_ERROR);
                    serverResponseLabel.setHTML(caught.getMessage());
                    dialogBox.center();
                    closeButton.setFocus(true);
                  }

                  public void onSuccess(String result) {
                    dialogBox.setText("Remote Procedure Call");
                    serverResponseLabel.removeStyleName("serverResponseLabelError");
                    serverResponseLabel.setHTML(result);
                    dialogBox.center();
                    closeButton.setFocus(true);
                  }
                });
     
          } else {
             // User is not authorized!
             // Show the RPC error message to the user
                   dialogBox.setText("Remote Procedure Call - Failure");
                   serverResponseLabel.addStyleName("serverResponseLabelError");
                   serverResponseLabel.setHTML("You are not authorized!");
                   dialogBox.center();
                   closeButton.setFocus(true);
          }
      }
  });
  
  try {
   rb.send();
  } catch (RequestException re) {
   //Log.error("Exception in sending RequestBuilder: " + re.toString());
  }
  
      }
    }
What we did here is wrap our RPC service request inside GWT's RequestBuilder. Also, we called a custom URL that points to a JSP page to verify the authentication of the user. This JSP page as declared in the spring-security.xml has the following access level:

<security:intercept-url pattern="/krams/main/admin" access="hasRole('ROLE_ADMIN')"/>
This URL is only accessible if the user is authenticated and has admin rights. If we can access this then the current user has the rights.

What is RequestBuilder?
Provides the client-side classes and interfaces for making HTTP requests and processing the associated responses.

Most applications will be interested in the Request, RequestBuilder, RequestCallback and Response classes.

Source: http://www.gwtapps.com/doc/html/com.google.gwt.http.client.html

For more info about RequestBuilder, please visit this link.

Notice in the code, we only execute the GreetingService's greetServer() method when the status code is equal to 200
if (response.getStatusCode() == 200) {
  ...
}
If the status code is different, we send a customized error message:
...
else {
 // User is not authorized!
 // Show the RPC error message to the user
 dialogBox.setText("Remote Procedure Call - Failure");
 serverResponseLabel.addStyleName("serverResponseLabelError");
 serverResponseLabel.setHTML("You are not authorized!");
 dialogBox.center();
 closeButton.setFocus(true);
}
Let's run our application again and verify the results.


We now have a friendlier error message. That's great. But how do we log in? We can't.

We have to create a custom login screen either via a JSP page (if you have Spring MVC enabled) or via GWT (this means you'll need to know how to use widgets, similar with the greet server interface). To make our lives easier, we'll use a JSP page because in the first place Spring MVC is already enabled in the web.xml, spring-servlet.xml, and applicationContext.xml

This means we need to add corresponding controllers and JSP pages. If you to refresh your memory on Spring MVC and integration with Spring Security, please visit the following tutorial: Spring Security 3 - MVC Integration Tutorial.

Here are our controllers:

LoginLogoutController
/**
 * 
 */
package org.krams.tutorial.controller;


import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
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 login or denied page depending on the URI template
 */
@Controller
@RequestMapping("/auth")
public class LoginLogoutController {
        
 protected static Logger logger = Logger.getLogger("controller");

 /**
  * Handles and retrieves the login JSP page
  * 
  * @return the name of the JSP page
  */
 @RequestMapping(value = "/login", method = RequestMethod.GET)
 public String getLoginPage(@RequestParam(value="error", required=false) boolean error, 
   ModelMap model) {
  logger.debug("Received request to show login page");

  // Add an error message to the model if login is unsuccessful
  // The 'error' parameter is set to true based on the when the authentication has failed. 
  // We declared this under the authentication-failure-url attribute inside the spring-security.xml
  // However this doesn't seem to work when GWT is enabled
  if (error == true) {
   // Assign an error message
   model.put("error", "You have entered an invalid username or password!");
  } else {
   model.put("error", "");
  }
  
  // This will resolve to /WEB-INF/jsp/loginpage.jsp
  return "loginpage";
 }
 
 /**
  * Handles and retrieves the denied JSP page. This is shown whenever a regular user
  * tries to access an admin only page.
  * 
  * @return the name of the JSP page
  */
 @RequestMapping(value = "/denied", method = RequestMethod.GET)
  public String getDeniedPage() {
  logger.debug("Received request to show denied page");
  
  // This will resolve to /WEB-INF/jsp/deniedpage.jsp
  return "deniedpage";
 }
}
MainController
package org.krams.tutorial.controller;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Handles and retrieves the common or admin page depending on the URI template.
 * A user must be log-in first he can access these pages.  Only the admin can see
 * the adminpage, however.
 */
@Controller
@RequestMapping("/main")
public class MainController {

 protected static Logger logger = Logger.getLogger("controller");
 
 /**
  * Handles and retrieves the common JSP page that everyone can see
  * 
  * @return the name of the JSP page
  */
    @RequestMapping(value = "/common", method = RequestMethod.GET)
    public String getCommonPage() {
     logger.debug("Received request to show common page");
    
     // Do your work here. Whatever you like
     // i.e call a custom service to do your business
     // Prepare a model to be used by the JSP page
     
     // This will resolve to /WEB-INF/jsp/commonpage.jsp
     return "commonpage";
 }
    
    /**
     * Handles and retrieves the admin JSP page that only admins can see
     * 
     * @return the name of the JSP page
     */
    @RequestMapping(value = "/admin", method = RequestMethod.GET)
    public String getAdminPage() {
     logger.debug("Received request to show admin page");
    
     // Do your work here. Whatever you like
     // i.e call a custom service to do your business
     // Prepare a model to be used by the JSP page
     
     // This will resolve to /WEB-INF/jsp/adminpage.jsp
     return "adminpage";
 }
}
Here are the JSP pages.

loginpage.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>

<%@ 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>Insert title here</title>
</head>
<body>

<h1>Login</h1>

<div id="login-error">${error}</div>

<form action="../../j_spring_security_check" method="post" >

<p>
 <label for="j_username">Username</label>
 <input id="j_username" name="j_username" type="text" />
</p>

<p>
 <label for="j_password">Password</label>
 <input id="j_password" name="j_password" type="password" />
</p>

<input  type="submit" value="Login"/>        
 
</form>

</body>
</html>
deniedpage.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">
<title>Insert title here</title>
</head>
<body>
<h1>Access Denied!</h1>
<p>Only admins can see this page!</p>
</body>
</html>
adminpage.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">
<title>Insert title here</title>
</head>
<body>
<h1>Admin Page</h1>
<p>Only admins have access to this page.</p>
<p>Curabitur quis libero elit, dapibus iaculis nisl. Nullam quis velit eget odio 
adipiscing tristique non sed ligula. In auctor diam eget nisl condimentum laoreet..</p>
</body>
</html>
commonpage.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">
<title>Insert title here</title>
</head>
<body>
<h1>Common Page</h1>
<p>Everyone has access to this page.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam ac velit et ante 
laoreet eleifend. Donec vitae augue nec sem condimentum varius.</p>
</body>
</html>

To login using the JSP page, open a new browser or a new tab. Then use the following URL:
http://127.0.0.1:8888/krams/auth/login
Here's the login page.

Notice there's an error ${error}. In a normal Spring MVC application, this should be hidden and only populated via a model object. I decided to include this error here anyway to remind my readers that integrating GWT and Spring have some disadvantages as well.

Try logging-in. Use john for the username and admin for the password.

You should see the following message:
HTTP ERROR: 200

Authentication success
RequestURI=/j_spring_security_check

Powered by Jetty://
We receive an HTTP error because the URL that we'd been redirected http://127.0.0.1:8888/j_spring_security_check doesn't exist for viewing. But notice we're authenticated.

Now, try sending a message again. Go the GWT application, and send a message. Here's what we should get:

That's it. We're created a simple GWT application that's secured by Spring Security. We've explored ways of integrating both technologies, including Spring MVC. We've also discussed how we can use RequestBuilder for requesting standard resources.

As a side note, you can create a GWT login-page that delegates the authentication via RequestBuilder. For example:
RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, "/j_spring_security_check");

rb.setHeader("Content-Type", "application/x-www-form-urlencoded");

rb.setRequestData("j_username=" + URL.encode(myusername + "&j_password=" + URL.encode("mypassword")));


Notice how it's similar with a standard JSP login page:
<form action="../../j_spring_security_check" method="post" >

<p>
 <label for="j_username">Username</label>
 <input id="j_username" name="j_username" type="text" />
</p>

<p>
 <label for="j_password">Password</label>
 <input id="j_password" name="j_password" type="password" />
</p>

<input  type="submit" value="Login"/>        
 
</form>

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-gwt/

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

You can run the project directly using the Maven GWT plugin.
mvn gwt: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 and GWT: Security via Spring Security ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share

Spring and GWT Integration using Maven and GWTHandler (Part 2)

In this tutorial we will continue what we had left off from Spring and GWT Integration using Maven and GWTHandler (Part 1). Last time we set-up a simple GWT project using Maven. We generated the Async interfaces and run the application using specialized Maven goals. Now we will start integrating Spring with our current GWT application.

Note: An updated version of this guide is available at Spring 3.1: GWT Maven Plugin and GWTHandler Integration

We start by declaring the necessary XML configurations. We will do a side-by-side (actually top-by-down) comparison of the old and new configurations. Let's begin with the web.xml.

web.xml (old)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <!-- Servlets -->
  <servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>org.krams.tutorial.server.GreetingServiceImpl</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/gwtmodule/greet</url-pattern>
  </servlet-mapping>

  <!-- Default page to serve -->
  <welcome-file-list>
    <welcome-file>gwtmodule.html</welcome-file>
  </welcome-file-list>

</web-app>
web.xml (new)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

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

 <!-- Front Controller for all GWT Spring based servlets -->
 <servlet>
  <servlet-name>gwt</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <!-- Front Controller for all Spring based servlets -->
 <servlet>
  <servlet-name>spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 <!-- Don't forget to declare a gwt-servlet.xml -->
 <servlet-mapping>
  <servlet-name>gwt</servlet-name>
  <url-pattern>gwtmodule/rpc/*</url-pattern>
 </servlet-mapping>
 
 <!-- Don't forget to declare a spring-servlet.xml -->
 <servlet-mapping>
  <servlet-name>spring</servlet-name>
  <url-pattern>krams/*</url-pattern>
 </servlet-mapping>

</web-app>
We've declared two servlets:
gwt-servlet - for displaying GWT 
spring-servlet - for displaying JSPs
We actually just need the gwt-servlet, but we set-up the spring-servlet here to demonstrate that we can combine JSPs and GWT in the same application

Here's gwt-servlet.xml

gwt-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<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">
 
 <!-- 
  The GWTHandler allows you to quickly map multiple RPC service beans to different URLs
  very similar to the way Spring's SimpleUrlHandlerMapping maps URLs to controllers. The
  mapped beans are internally wrapped into GWTRPCServiceExporter
  instances, with the notable difference that you cannot specify a service interface in the configuration
  and the service beans must implement the RemoteService interface (as a matter of fact there is
  a workaround even for that by providing your own implementation of a RPCServiceExporter -
  interested readers please consult the javadocs for GWTHandler).
  
  See 3.2 Publishing multiple beans - GWTHandler 
  http://gwt-widget.sourceforge.net/?q=node/54 -->
 
 <!--  If you wanna research further about annotation support with GWT Handler.
   See http://groups.google.com/group/gwt-sl/browse_thread/thread/f563b200aa0af307# -->
   
 <!-- Or create our own implementation: 
     Seehttp://groups.google.com/group/gwt-sl/msg/3677e59c4a7c2dee -->
   
  <!-- A GWT Spring bean -->   
 <bean id ="greetingService" class="org.krams.tutorial.server.GreetingServiceImpl" >
  <property name="springService" ref="springService" />
 </bean>
 
 <!-- A Spring bean-->
 <bean id="springService" class="org.krams.tutorial.service.SpringService" />
 
 <!-- The GWT handler. Watch out the mappings! -->
 <bean class="org.gwtwidgets.server.spring.GWTHandler">
  <property name="mappings">
   <map>
    <entry key="/greet" value-ref="greetingService"/></map>
  </property>
 </bean>
 
</beans>
Here we declared a GWTHandler. This is basically similar to SimpleUrlHandlerMapping where a URL is mapped to a particular bean. In order for GWTHandler to match the mapping request, we need to modify the GreetingService interface (I'll show you the old implementation and the new one)

GreetingService (old)
package org.krams.tutorial.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
  String greetServer(String name) throws IllegalArgumentException;
}

GreetingService (new)
package org.krams.tutorial.client;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

/**
 * The client side stub for the RPC service.
 */
@RemoteServiceRelativePath("rpc/greet")
public interface GreetingService extends RemoteService {
  String greetServer(String name) throws IllegalArgumentException;
}
We've changed the path from greet to rpc/greet. Remember in the web.xml we've declared a url pattern:
<!-- Don't forget to declare a gwt-servlet.xml -->
 <servlet-mapping>
  <servlet-name>gwt</servlet-name>
  <url-pattern>gwtmodule/rpc/*</url-pattern>
 </servlet-mapping>
Make sure you correctly map your paths! Let me summarize that for you:
web.xml declaration: gwtmodule/rpc/*
interface declaration: rpc/greet
gwt-servlet.xml declaration: /greet

For a thorough discussion of GWTHandler, see 3.2 Publishing multiple beans - GWTHandler at http://gwt-widget.sourceforge.net/?q=node/54

In this configuration we mapped the /greet URL to the greetingService bean.
<entry key="/greet" value-ref="greetingService"/></map>
This greetingService bean is actually the default service implementation that was provided to us when we created the application. It references GreetingServiceImpl. We need to modify this class!

Let me show you first the original implementation (the one that's auto-generated for us):

GreetingServiceImpl (auto-generated)
package org.krams.tutorial.server;

import org.krams.tutorial.client.GreetingService;
import org.krams.tutorial.shared.FieldVerifier;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
    GreetingService {

  public String greetServer(String input) throws IllegalArgumentException {
    // Verify that the input is valid.
    if (!FieldVerifier.isValidName(input)) {
      // If the input is not valid, throw an IllegalArgumentException back to
      // the client.
      throw new IllegalArgumentException(
          "Name must be at least 4 characters long");
    }

    String serverInfo = getServletContext().getServerInfo();
    String userAgent = getThreadLocalRequest().getHeader("User-Agent");

    // Escape data from the client to avoid cross-site script vulnerabilities.
    input = escapeHtml(input);
    userAgent = escapeHtml(userAgent);

    return "Hello, " + input + "!<br><br>I am running " + serverInfo
        + ".<br><br>It looks like you are using:<br>" + userAgent;
  }

  /**
   * Escape an html string. Escaping data received from the client helps to
   * prevent cross-site script vulnerabilities.
   *
   * @param html the html string to escape
   * @return the escaped string
   */
  private String escapeHtml(String html) {
    if (html == null) {
      return null;
    }
    return html.replaceAll("&", "&").replaceAll("<", "<").replaceAll(
        ">", ">");
  }
}
We need to remove the following in order for GWTHandler to recognize this class
extends RemoteServiceServlet
After you remove this reference, some parts of the code will get flagged. Just delete those, and replace it with this new implementation:

GreetingServiceImpl (new)
package org.krams.tutorial.server;

import org.krams.tutorial.client.GreetingService;
import org.krams.tutorial.service.SpringService;
import org.krams.tutorial.shared.FieldVerifier;

/**
 * The server side implementation of the RPC service.
 */
@SuppressWarnings("serial")
public class GreetingServiceImpl implements
    GreetingService {
  
  // Our custom Spring Service bean
  private SpringService springService;
  
  public String greetServer(String input) throws IllegalArgumentException {
    // Verify that the input is valid.
    if (!FieldVerifier.isValidName(input)) {
      // If the input is not valid, throw an IllegalArgumentException back to
      // the client.
      throw new IllegalArgumentException(
          "Name must be at least 4 characters long");
    }

    // Escape data from the client to avoid cross-site script vulnerabilities.
    input = escapeHtml(input);

    // Delegate to SpringService and return the result
    return springService.echo(input);
  }

  /**
   * Escape an html string. Escaping data received from the client helps to
   * prevent cross-site script vulnerabilities.
   *
   * @param html the html string to escape
   * @return the escaped string
   */
  private String escapeHtml(String html) {
    if (html == null) {
      return null;
    }
    return html.replaceAll("&", "&").replaceAll("<", "<").replaceAll(
        ">", ">");
  }

   /**
    * Retrieves the SpringService bean
    */
 public SpringService getSpringService() {
  return springService;
 }

 /**
  * Assigns the SpringService bean
 */
 public void setSpringService(SpringService springService) {
  this.springService = springService;
 }

}
Notice we've declared a custom Spring service bean and added getters and setters. Whenever an RPC request is made to the GWT GreetingService interface, the application calls the service-side implementation, GreetingServiceImpl. This is the standard process with GWT RPC. However, we provided another flow in this cycle. After calling the GreetingServiceImpl, it then delegates to a SpringService bean for the actual retrieval of the output.

We need to create a new package in the project that corresponds to the package we declared in the gwt-servlet.xml. In my case I created org.krams.tutorial.service and created the following Spring service bean:

SpringService
package org.krams.tutorial.service;

import org.apache.log4j.Logger;

public class SpringService {

 protected static Logger logger = Logger.getLogger("service");
 
 public String echo(String msg) {
  logger.debug("Entering SpringService");
  
  return "Hello " + msg + " from Spring!";
 }
}
Here's the project structure:

Let's continue with the remaining XML configurations.

Here's spring-servlet.xml

spring-servlet.xml
<?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" 
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
 
 <!-- Declare a view resolver -->
 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" 
      p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

</beans>

Next, we declare the applicationContext.xml

applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:p="http://www.springframework.org/schema/p"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.0.xsd
         http://www.springframework.org/schema/mvc 
         http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd" >

 <!-- 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!  
  Commented out because of conflict with GWTHandler 
  Instead we declare the elements declaratively!-->
 <!-- <mvc:annotation-driven /> -->
 
 <!-- This allows us to use @Controller and @Service annotations -->
 <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

 <!-- Commented out because of conflict with GWTHandler. You lost @ResponseBody,
  @RequestBody, @PathVariable
 <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="webBindingInitializer">
   <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"/>
  </property>
  <property name="messageConverters">
   <list>
    <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
    <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
    <bean class="org.springframework.http.converter.FormHttpMessageConverter" />
    <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
   </list>
  </property>
 </bean> -->

 <!-- Not really needed but it's part of the mvc:annotation-driven tag-->
 <bean id="conversion-service" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
</beans>
The applicationContext.xml contains some interesting information. All the beans we've declared here aren't really required for our GWT application. However these beans enable us to use special annotations like @Service and @Controller. This allows us to have a Spring MVC application separate from the GWT application within this same project.

However, we cannot use the mvc:annotation-driven tag here because of a conflict with the GWTHandler. There are possible workarounds for these (I suggest you look at the comments in the configuration). Since we cannot declare the mvc:annotation tag, we declare its equivalent beans separately. However it turns out the real source of the conflict is the AnnotationMethodHandlerAdapter. It swallows all requests, and ignores the GWTHandler. Please bear that in mind.

Let's look again at the current project structure:

Notice we have four XML configurations. But also I have a highlighted the gwt-sl-1.1.jar. We need to create a lib folder and manually place a copy of this jar. Why? Because the author of this library doesn't support Maven yet. (See the discussion here)

That's it! We're done with all the required configuration. We've added and modified our classes as well. Let's run this application and see what we'll get.

To run the application, use the Maven gwt:run goal we've created from the previous tutorial: Spring and GWT Integration using Maven and GWTHandler (Part 1).

This is what see after running the application:


We changed the greeting from the typical server info message to a custom echo message.

Before we end this tutorial, let's preview our pom.xml. We'll compare the initial pom and the final pom.

pom.xml (initial, auto-generated)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <!-- POM file generated with GWT webAppCreator -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.krams.tutorial</groupId>
  <artifactId>spring-gwt-integration</artifactId>
  <packaging>war</packaging>
  <version>1.0.0-SNAPSHOT</version>
  <name>GWT Maven Archetype</name>

  <properties>
    <!-- Convenience property to set the GWT version -->
    <gwtVersion>2.1.0</gwtVersion>
    <!-- GWT needs at least java 1.5 -->
    <maven.compiler.source>1.5</maven.compiler.source>
    <maven.compiler.target>1.5</maven.compiler.target>
    <webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-servlet</artifactId>
      <version>2.1.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>2.1.0</version>
      <scope>provided</scope>
    </dependency>  
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.7</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <!-- Generate compiled stuff in the folder used for developing mode -->
    <outputDirectory>${webappDirectory}/WEB-INF/classes</outputDirectory>

    <plugins>

      <!-- GWT Maven Plugin -->
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <version>2.1.0-1</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>test</goal>
              <goal>i18n</goal>
              <goal>generateAsync</goal>
            </goals>
          </execution>
        </executions>
        <!-- Plugin configuration. There are many available options, see gwt-maven-plugin 
          documentation at codehaus.org -->
        <configuration>
          <runTarget>gwtmodule.html</runTarget>
          <hostedWebapp>${webappDirectory}</hostedWebapp>
          <i18nMessagesBundle>org.krams.tutorial.client.Messages</i18nMessagesBundle>
        </configuration>
      </plugin>

      <!-- Copy static web files before executing gwt:run -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>exploded</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <webappDirectory>${webappDirectory}</webappDirectory>
        </configuration>
      </plugin>

    </plugins>
  </build>

</project>

pom.xml (final)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <!-- POM file generated with GWT webAppCreator -->
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.krams.tutorial</groupId>
  <artifactId>spring-gwt-integration</artifactId>
  <packaging>war</packaging>
  <version>1.0.0-SNAPSHOT</version>
  <name>GWT Maven Archetype</name>

  <properties>
    <!-- Convenience property to set the GWT version -->
    <gwtVersion>2.1.0</gwtVersion>
    <!-- GWT needs at least java 1.5 -->
    <maven.compiler.source>1.5</maven.compiler.source>
    <maven.compiler.target>1.5</maven.compiler.target>
    <webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-servlet</artifactId>
      <version>2.1.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>2.1.0</version>
      <scope>provided</scope>
    </dependency>  
   <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.8.1</version>
    <type>jar</type>
    <scope>compile</scope>
   </dependency>
   <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-web</artifactId>
     <version>3.0.5.RELEASE</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-core</artifactId>
     <version>3.0.5.RELEASE</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.14</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-tx</artifactId>
     <version>3.0.5.RELEASE</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>jstl</groupId>
     <artifactId>jstl</artifactId>
     <version>1.1.2</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>taglibs</groupId>
     <artifactId>standard</artifactId>
     <version>1.1.2</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>3.0.5.RELEASE</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
    <dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-aop</artifactId>
     <version>3.0.5.RELEASE</version>
     <type>jar</type>
     <scope>compile</scope>
    </dependency>
  </dependencies>

  <build>
    <!-- Generate compiled stuff in the folder used for developing mode -->
    <outputDirectory>${webappDirectory}/WEB-INF/classes</outputDirectory>

    <plugins>

      <!-- GWT Maven Plugin -->
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <version>2.1.0-1</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
              <goal>test</goal>
              <goal>i18n</goal>
              <goal>generateAsync</goal>
            </goals>
          </execution>
        </executions>
        <!-- Plugin configuration. There are many available options, see gwt-maven-plugin 
          documentation at codehaus.org -->
        <configuration>
          <runTarget>gwtmodule.html</runTarget>
          <hostedWebapp>${webappDirectory}</hostedWebapp>
          <i18nMessagesBundle>org.krams.tutorial.client.Messages</i18nMessagesBundle>
        </configuration>
      </plugin>

      <!-- Copy static web files before executing gwt:run -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
        <executions>
          <execution>
            <phase>compile</phase>
            <goals>
              <goal>exploded</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <webappDirectory>${webappDirectory}</webappDirectory>
        </configuration>
      </plugin>

    </plugins>
  </build>

</project>

That's it. We're done. I hope you learned something new with this tutorial.

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-gwt/

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

You can run the project directly using the Maven GWT plugin.
mvn gwt: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 and GWT Integration using Maven and GWTHandler (Part 2) ~ Twitter FaceBook

Subscribe by reader Subscribe by email Share