Monday, May 21, 2012

Spring MVC 3: Part 1 - Listing and Viewing

Overview:
This will be a TWO part series article that looks at Spring MVC 3. To better understand the usage of the framework + to keep the tutorial short and keep the reader interested, I have split this into TWO parts.

Part 1: Listing and Viewing Person Details (Retrieve)
Part 2: Form Processing ( Create, Update and Delete)

The example used for both the articles will be same, and will be concentrating on CRUD operations. The example will be a People Management Application.

The sample code for BOTH these articles are here.

Part 1: Listing and Viewing Person Details (Retrieve)

Application Demo:


Listing All People Details.

View Details of a Person


Project Structure: Following is the project structure used in the article (Maven wed application).

Java package structure

Web app structure

Step 1: Creating the Web Project.

mvn archetype:generate -DgroupId=org.fazlan -DartifactId=org.fazlan.spring.mvc -Dversion=1.0.0-SNAPSHOT -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

Step 2:Updating the Maven Dependencies (pom.xml)

 <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">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>org.fazlan</groupId>  
   <artifactId>org.fazlan.spring.mvc</artifactId>  
   <packaging>war</packaging>  
   <version>1.0.0-SNAPSHOT</version>  
   <name>org.fazlan.spring.mvc</name>  
   <url>http://maven.apache.org</url>  
   <dependencies>  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>3.8.1</version>  
       <scope>test</scope>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet</groupId>  
       <artifactId>servlet-api</artifactId>  
       <version>2.5</version>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet.jsp</groupId>  
       <artifactId>jsp-api</artifactId>  
       <version>2.1</version>  
       <scope>provided</scope>  
     </dependency>  
     <dependency>  
       <groupId>taglibs</groupId>  
       <artifactId>standard</artifactId>  
       <version>1.1.2</version>  
     </dependency>  
     <dependency>  
       <groupId>commons-logging</groupId>  
       <artifactId>commons-logging</artifactId>  
       <version>1.1.1</version>  
     </dependency>  
     <dependency>  
       <groupId>javax.servlet</groupId>  
       <artifactId>jstl</artifactId>  
       <version>1.2</version>  
     </dependency>  
     <!-- Spring 3 dependencies -->  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-core</artifactId>  
       <version>${org.springframework.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-web</artifactId>  
       <version>${org.springframework.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.springframework</groupId>  
       <artifactId>spring-webmvc</artifactId>  
       <version>${org.springframework.version}</version>  
     </dependency>  
   </dependencies>  
   <build>  
     <finalName>springmvc</finalName>  
     <plugins>  
       <plugin>  
         <groupId>org.mortbay.jetty</groupId>  
         <artifactId>maven-jetty-plugin</artifactId>  
         <version>6.1.10</version>  
       </plugin>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-compiler-plugin</artifactId>  
         <version>2.4</version>  
         <configuration>  
           <source>1.6</source>  
           <target>1.6</target>  
         </configuration>  
       </plugin>  
     </plugins>  
   </build>  
   <properties>  
     <org.springframework.version>3.1.1.RELEASE</org.springframework.version>  
   </properties>  
 </project>  

Step 3: Defining the Spring MVC Model, Backend Logic.

Person.java - represent the M in MVC.

 package org.fazlan.spring.mvc.model;  
 public class Person {  
   private Long id;  
   private String firstName;  
   private String lastName;  
   public Person() {  
   }  
   public Person(long id, String firstName, String lastName) {  
     this.id = id;  
     this.firstName = firstName;  
     this.lastName = lastName;  
   }  
   public boolean isNew() {  
     return id == null;  
   }  
   public Long getId() {  
     return id;  
   }  
   public void setId(Long 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;  
   }  
   @Override  
   public boolean equals(Object o) {  
     if (this == o) return true;  
     if (!(o instanceof Person)) return false;  
     Person person = (Person) o;  
     return id == person.id && !(firstName != null ? !firstName.equals(person.firstName) : person.firstName != null) && !(lastName != null ? !lastName.equals(person.lastName) : person.lastName != null);  
   }  
   @Override  
   public int hashCode() {  
     int result = (int) (id ^ (id >>> 32));  
     result = 31 * result + (firstName != null ? firstName.hashCode() : 0);  
     result = 31 * result + (lastName != null ? lastName.hashCode() : 0);  
     return result;  
   }  
 }  

IManager.java and PersonManager - represents the backend service implementation.

IManager.java

 package org.fazlan.spring.mvc.manager;  

 import java.util.List;  

 public interface IManager<T> {  
   List<T> getAll();  
   T get(Long id); 
 }  

PersonManager.java

  package org.fazlan.spring.mvc.manager; 
  
  import org.fazlan.spring.mvc.model.Person;   
  import java.util.ArrayList;   
  import java.util.HashMap;   
  import java.util.List;   
  import java.util.Map;  
 
  public class PersonManager implements IManager&lt;Person&gt;{   
   private final Map&lt;Long, Person&gt; PERSON_MAP = new HashMap&lt;Long, Person&gt;();   
   private static Long INDEX = 1L;   
   {   
    PERSON_MAP.put(INDEX, new Person(INDEX, "First Name", "Second Name"));   
   }   
   @Override   
   public List&lt;Person&gt; getAll() {   
    return new ArrayList&lt;Person&gt;(PERSON_MAP.values());   
   }   
   @Override   
   public Person get(Long id) {   
    return PERSON_MAP.get(id);   
   }
  }   

Step 4: Defining the Spring MVC Controller.
A controller receives web requests, performs business logic, and populates the model (Person.java) for the view to present.

 package org.fazlan.spring.mvc.controllers;
  
 import org.fazlan.spring.mvc.manager.IManager;  
 import org.fazlan.spring.mvc.model.Person;  
 import org.fazlan.spring.mvc.validators.PersonValidator;  
 import org.springframework.beans.factory.annotation.Autowired;  
 import org.springframework.beans.propertyeditors.StringTrimmerEditor;  
 import org.springframework.stereotype.Controller;  
 import org.springframework.ui.Model;  
 import org.springframework.validation.BindingResult;  
 import org.springframework.web.bind.WebDataBinder;  
 import org.springframework.web.bind.annotation.*;  
 import org.springframework.web.bind.support.SessionStatus;  

 import java.util.List;  

 @Controller  
 @SessionAttributes("person")  
 public class PersonForm {  
   @Autowired  
   private IManager<Person> manager;
  
   @RequestMapping("/list")  
   @ModelAttribute("personList")  
   public List<Person> getAll() {  
     return manager.getAll();  
   }  

   @RequestMapping("/view")  
   public Person get(@RequestParam(value = "id", required = true) Long personId) {  
     return manager.get(id);  
   }    
 }  

The above class defined the controller to handle the requests for managing a Person. For now, let's look at the following elements,

@Controller
Designates a POJO class as a web controller. Spring can automatically detect controller classes using class path scanning, and make them available to receive requests from end users. Equivalent to implementing the Controller interface in Spring MVC 2.

@RequestMapping
This annotation maps web request URLs to Java classes and methods. The above code snippet maps the URL paths /list and /view to the getAll() and get() methods respectively.

@ModelAttribute
This annotation binds the return value of a method to a named attribute in the view(JSP). The getAll() method returns a list of Person JavaBeans, and the @ModelAttribute annotation binds it to an attribute called personList in the view, for subsequent retrieval by the view.

The get() method also returns a Person JavaBean into the view, but this time no @ModelAttribute annotation is specified. In this case, Spring uses the non-qualified class name, 'person', as the attribute name.

For instance, the get() method could have annotated as below for better readability of the code.

 @RequestMapping("/view")   
 @ModelAttribute("person")   
 public Person get(@RequestParam(value = "id", required = true) Long personId) {   
    return manager.get(id);   
 }   

@RequestParam
This annotation binds HTTP request parameters to method arguments in the controller.

In above, the get() method needs to know the identifier (personId) of the Person JavaBean that it is to be returned. The @RequestParam annotation takes the 'id' request parameter from the URL, and maps it to the personId argument of the method. The parameter is mandatory (required=true) so the URL must be ending like /view.html?id=1, or an exception will be generated.

Step 5: Defining the Spring MVC View
After the controller process the request, the view represent the data from the model. We use JSPs and JSTL(Java Standard Tag Library) to display the data in our application.

Logical Views

Firstly, we must define the logical view names to let Spring know, which view has to be rendered. By default, it is derived from the URL path of our request.

For example:
/list.html transformed to view name called list
/view.html transformed to view name called view

If needed, we can override the default by returning a specific view name from our controller, but for now we can just rely on the default.

Physical Views

list.jsp
The page displays a list of people. Each person record is a link to a page which shows further details about that person. We use JSTL custom tags and the JSP expression language to retrieve data from the model and construct the HTML table.

 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
 <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
 <body>  
 <div>  
   <h1><spring:message code="person.details"/></h1>  
   <table class="gradient">  
     <tr>  
       <th><spring:message code="person.firstName"/></th>  
       <th><spring:message code="person.lastName"/></th>  
       <th><spring:message code="person.action"/></th>  
     </tr>  
     <c:forEach items="${personList}" var="person">  
       <tr>  
         <td>${person.firstName}</td>  
         <td>${person.lastName}</td>  
         <td>  
           <a href="view.html?id=${person.id}"> <spring:message code="navigation.view"/> </a>/<a  
             href="add.html?id=${person.id}"><spring:message code="navigation.edit"/></a>  
         </td>  
       </tr>  
     </c:forEach>  
   </table>  
 </div>  
 <a href='add.html'><spring:message code="navigation.add"/></a>  
 </body>  

The personList collection, which was added to the model by the getAll() method in the PersonForm class, is now accessible by the JSP expression language. The forEach tag iterates over the collection of Person JavaBeans, using the firstName and lastName property for the display value, and the id property in the link to the details page.

view.jsp
The page displays the details of an individual.

 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
 <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 
 <body>  
   <h1><spring:message code="person.details"/></h1>  
   <table class="gradient">  
    <tr>  
     <td><spring:message code="person.firstName"/></td>  
     <td>${person.firstName}</td>  
    </tr>  
    <tr>  
     <td><spring:message code="person.lastName"/></td>  
     <td>${person.lastName}</td>  
    </tr>  
   </table>  
   <a href="list.html"><spring:message code="navigation.back"/></a>  
 </body>  

The data is retrieved from the Person JavaBean, which was added to the model by the get() method in the PersonForm class

View Resolver

After defining logical and physical view names, a view resolver is used to map them and display. Spring provides several view resolvers for technologies such as Velocity templates and XSLT, but we will use Spring's InternalResourceViewResolver class to map the view name to a physical JSP file.

For example:
list is mapped to /jsp/list.jsp
view is mapped to /jsp/view.jsp

Following is how logical view names are mapped to physical files. In this case, the file name will be made up of:

    /jsp/ + physical_view_name + .jsp
For example, a view name list will result in the /jsp/list.jsp file.

This separation of logical view names from view resolvers allows the controllers to produce data models, without being tied to a particular view technology.

Step 6: Spring MVC Configuration
In Spring, front controller pattern called DispatcherServlet dispatches HTTP requests to the appropriate controller. This we define in the web.xml.

web.xml
To work with the latest JSTL and JSP-API, you need to use the latest version of web.xml (as highlighted).

 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
 xmlns="http://java.sun.com/xml/ns/javaee"  
 xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee  
 http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">  
  <display-name>Archetype Created Web Application</display-name>  
   <servlet>  
     <servlet-name>springmvc</servlet-name>  
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
     <load-on-startup>1</load-on-startup>  
   </servlet>  
   <servlet-mapping>  
     <servlet-name>springmvc</servlet-name>  
     <url-pattern>*.html</url-pattern>  
   </servlet-mapping>  
   <welcome-file-list>  
     <welcome-file>  
      /list.html  
     </welcome-file>  
    </welcome-file-list>  
 </web-app>  

Spring Configuration File (springmvc.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"  
     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">  

   <context:annotation-config/>  
   <context:component-scan base-package="org.fazlan.spring.mvc"/>  

   <bean id="personValidator" class="org.fazlan.spring.mvc.validators.PersonValidator"/>  
   <bean id="personManager" class="org.fazlan.spring.mvc.manager.PersonManager"/>  
   <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>  
     <property name="prefix" value="/jsp/"/>  
     <property name="suffix" value=".jsp"/>  
   </bean>  
   <!-- Access resource bundles with the specified basename -->  
   <bean id="messageSource"  
      class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
     <property name="basename" value="msg-properties/messages"/>  
   </bean>  
 </beans>  

Step 7: Building and Deploying the Application

$ mvn clean install
$ mvn jetty:run

mvn:jetty:run - would run the web app on the jetty server.

Now, you can access the application with the following URL.
http://localhost:8080/org.fazlan.spring.mvc/list.html

Summary:
The Part 1 of the Spring MVC 3 article focused on how to use the framework for retrieving data and rendering on JSPs. The next part will concentrate on how to handle HTML Form processing.

The sample code for BOTH these articles are here.

That's all Folks!!!

No comments:

Post a Comment