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