Tuesday, August 12, 2014

Spring MVC before Spring 3.0

****This article is relevant for Spring 2.5 and lesser version. Spring 3.0 has an annotation way of doing things.*****

Spring MVC is a web framework to handle web requests using Model View Controller pattern. Spring MVC works around a DisptacherServlet which handles all the request. If you take any web request than the fundamental steps required to process a request is as follows. The request is recieved in the form of Http request from client browser. The request is converted into a HttpRequest object and passed to a Servlet. In the case of Spring it is DisptacherServlet. The DispatcherServlet consults a handler mapping and decides the controller which is going to process the request. The request is processed by the controller and it returns a ModelAndView object. The ModelAndView contains both the Model and View. Model contains the data and view contains a logical id which can be resolved into an actual view which in most cases is a jsp file though there are many choices here. The view uses the Model data and generates the response which is returned back to the client browser.

Let's make a MVC application and look into differenet aspects of application. In this web application we have two page. In first page we will take the name of student and save it. The other page will list the Students.

<?xml version="1.0" encoding="UTF-8"?>
<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>SpringMVC</display-name>

<!-- The parameter tells about the location of configuration XML. Usually
    all the data access beans and service layer beans are kept here. 
             You can register more than one XML here. 
             For multiple context file, use comma delimiter. 
             Please be careful that in case of multiple bean having same name the
             later one will override the earlier one. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/applicationContext.xml</param-value>
</context-param>

<!-- The listener is reponsible for building the spring container. 
             It looks for all configuration XML as defined by parameter contextConfigLocation 
             and also looks for a configuration which is named as Dispatch Servlet name. 
             In this case it will be named as springapp-servlet.xml -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>

<!-- Dispatcher Servlet which traps all the request targeted for Spring MVC -->
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Mapping for the request. It can be anything -->
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
</web-app>

Now let's write the data access layer code. This is similar to Spring JDBC? classes . Right now we will be calling the data access layer code directly from front layer. In real life you might want to put one more layer of indirection by putting a service layer in the middle. Let's write the domain model class and the DAO access layer.
Student.java

/**
 * Java bean which will be used to save and retrieve data.
 *
 */
public class Student {
protected String name;
//Getters and setters
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

The DAO interface.

public interface StudentDao {
public void saveStudent(Student student);
public List<Student> getAllStudents();
}

The implementation of DAO. We will use plain JDBC at the moment.

public class StudentJdbcDao implements StudentDao {

protected SimpleJdbcTemplate simpleJdbcTemplate;
public void setSimpleJdbcTemplate(SimpleJdbcTemplate simpleJdbcTemplate) {
this.simpleJdbcTemplate = simpleJdbcTemplate;
}

@Override
public void saveStudent(Student student) {
simpleJdbcTemplate.update("insert into STUDENT (name) values (?)",student.getName());

}

@Override
public List<Student> getAllStudents() {
return simpleJdbcTemplate.query 
("Select name as Name from Student",
 new ParameterizedRowMapper<Student>(){
         public Student mapRow(ResultSet rs,int rowNum)
          throws SQLException {
         Student student = new Student();
         student.setName(rs.getString("Name"));
         return student;          
         }
 }
);
}
}

Register the dao class as bean in Spring. We will register them in applicationContext.xml. This is the configuration file which is referred by Spring from contextConfigLocation in web.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!-- The Dao class -->
<bean id="studentDao" class="com.oyejava.springmvc.StudentJdbcDao">
<property name="simpleJdbcTemplate" ref="jdbcTemplate" />
</bean>

<!-- Template class to access JDBC code -->
<bean id="jdbcTemplate"
class="org.springframework.jdbc.core.simple.SimpleJdbcTemplate">
<constructor-arg ref="dataSource" />
</bean>

<!-- Configuration for the data source -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"    
     destroy-method="close">
   <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
   <property name="url" value="jdbc:hsqldb:hsql://localhost" />
   <property name="username" value="sa" />
   <property name="password" value="" />
</bean>
</beans>

Also let's write another configuration XML in WEB-INF directory. The name of this file will be springapp-servlet.xml. The pattern is <Dispatcher Servlet name in web.xml>-servlet.xml.
springapp-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-2.5.xsd">
<bean id="studentListController"
class="com.oyejava.springmvc.StudentListController">
<property name="studentDao" ref="studentDao" />
</bean>

    <!-- command class and command name are used to retrieve and set the 
value as name value pair in HttpRequest and Response. The form view
tells that when the request comes for this Controller than which
form to display in which user input can be taken. -->
<bean id="studentCreateController"
class="com.oyejava.springmvc.StudentCreateController">
<property name="studentDao" ref="studentDao" />
<property name="formView" value="createStudent" />
<property name="commandName" value="student" />
<property name="commandClass" value="com.oyejava.springmvc.Student" />
</bean>

<bean id="simpleUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/studentList.htm">studentListController</prop>
<prop key="/createStudent.htm">studentCreateController</prop>
</props>
</property>
</bean>

<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>

</beans>

The spring context made of using the configuarion as referred by contextConfigLocation becomes the Global application context. The context created by configuration in springapp-Servlet context becomes the child context and refers to the bean in global application context. For example the studentCreateController bean refers to studentDao. studentDao bean is present in global application context.

The Web application startup process is that the Servlet container initializes the web application and then fires the contextInitialized event. ContextLoaderListener, which is a Listener, receives the event and creates the global WebApplicationContext. This is placed in ServletContext. Than the DispatcherServlet is initialized, creating its own WebApplicationContext and nesting it inside the global WebApplicationContext. Dispatcher servlet brings up all the components and does the wiring of dependencies. Now web application is ready to serve the request.

Let's now look into the controller class.

/**
 * The controller class which is used to take the user input and 
 * process the data at the backend
 */
public class StudentCreateController extends SimpleFormController {

protected StudentDao studentDao;

public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}

//The is object which is used to set the values when the form is 
//displayed first time
protected Object formBackingObject(HttpServletRequest request)
throws Exception {
Student student = new Student();
student.setName("Default Name");
return student;
}

//This method is called when the form is submitted by the user.
//The command class is Student so Spring automatically parses the
//HttpRequest object, retrieves the name value pair out of it and
//sets the properties in the command object.
protected ModelAndView onsubmit(Object command) throws Exception {
  Student student = (Student) command;
  studentDao.saveStudent(student);
  return new ModelAndView("redirect:/studentList.htm");
}
}

Also the controller for listing the student looks like:

//Listing of Students

public class StudentListController extends AbstractController {

   protected StudentDao studentDao;
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
   }
   // This issues a request to database through data access layer and
   //gets the list of students. The list of students is put inside a 
   //ModelAndView Object
    protected ModelAndView handleRequestInternal(HttpServletRequest arg0,
HttpServletResponse arg1) throws Exception {
     List<Student> studentList = studentDao.getAllStudents();

    //studentList - This is the logical view id and based on view resolve
    //will get converted into a physical view which can be a jsp file
    //students - This is the name of the parameter against which the list 
    //          will be stored.
    //This will be used in jsp file to access the student list object.
    //studentList - studentList object.
 return new ModelAndView("studentList","students",studentList);
    }
}

Controllers

We saw above two examples of Spring controller. Spring comes with a handful of controllers which are used to handle different kind of situations. The different controllers in Spring are:

  • AbstractController : It's the basic Controller and gives access to underlying HttpRequest and HttpResponse object. The ModelAndView object is returned from it. However you can build the response yourself, in that case return null in place of ModelAndView object.
  • MultiActionController : This controller can handle multiple requests using one Controller. The different request can be mapped to different methods of the controller.
  • AbstractCommandController : This follows command pattern. The request object is parsed and the values retrieved from request object is set to the command object. This controller is good for list kind of functioanlity where serach parameters can be set to command objects.
  • AbstractFormController : Provides Form submission support.
  • SimpleFormController : Provides more support for form submission. Provides support similar to AbstractFormController and beyond that supports the initial view to be shown and the view to which the request should go after success.
  • AbstractWizardFormController : Provides support for wizard kind of situation where the user can be directed to follow a pattern of pages to execute a specific task or workflow. For example booking of online tickets.


Handler Mapping

Now let's understand how DispatcherServlet comes to know that which controller should be invoked for a particular request. Spring again delegates this task to a handler mapper, which helps the DispatcherServlet to do the job. For example in the springapp-servlet.xml we have used the handler mapper with name simpleUrlMapping. The property list tells that for which type of url pattern which controller needs to be invoked. Spring again comes up with a handful of mapper adn you can have more than one mapper also existing. Differemnt handler mapping that come with Spring are:

  • BeanNameUrlHandlerMapping - The bean name is used as a URL pattern. For example we can configure our StudentListController as


<bean id="/studentList.htm" class="com.oyejava.springmvc.StudentListController">
   <property name="studentDao" ref="studentDao" />
</bean>

  • SimpleUrlHandlerMapping - As we have used in the example above. Maps the pattern to the controller. It is the preferred approach.
  • ControllerClassNameHandlerMapping – Maps controller to URL’s by using the controller’s class name as the basis for the URL. So our StudentListController will be mapped to /studentList.htm
View Resolver

Once the controller has processed the request and returns a logical view we need to map the logical view to the physcial view. In this case the View resolver comes into picture. The different view resolver that are provided by Spring are:

  • AbstractCachingViewResolver - Provides caching of view.
  • XmlViewResolver - Views are mapped using a XML file. The default file is WEB-INF/views.xml.
  • ResourceBundleViewResolver 0 Views are defined in properties file. The default properties file is views.properties.
  • UrlBasedViewResolver - Used when logical view directly map to the physical view.
  • InternalResourceViewResolver - As used in example above. Resolves the view as per the configuration.

To complete the exercise let's put the jsp files which are our actual views.
createStudent.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<body>
<form:form commandName="student">
<label for="name">Name:</label>
<form:input path="name" />
<input type="submit" value="Register" />
</form:form>
</body>
studentList.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<body>
<c:forEach items="${students}" var="student">
<tr>
<td>${student.name} <br/></td>
</tr>
</c:forEach>
</body>
Host the application on a server and hit the url of the application. You will be presented with a create student page and on submission will be taken to the listing of students.

Interceptor
Interceptors are like servlet filters but they act in front of controllers. They have three methods which can overridden. One method is executed before the handler, second one is called after the handler and third method is called when the complete request is finished.
Let' write the interceptor.
LoggingInterceptor.java :

public class LoggingInterceptor extends HandlerInterceptorAdapter {

private static Logger log = Logger.getLogger(LoggingInterceptor.class);

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// TODO Auto-generated method stub
log.info("Entered for processing request");
return true;
}

@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
log.info("Exited for processing request");
}
}
Register the interceptor in springapp-servlet.xml and register it with the handler:

<bean id="loggingInterceptor" class="com.oyejava.springmvc.LoggingInterceptor" />

<bean id="simpleUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="interceptors">
<list>
<ref local="loggingInterceptor" />
</list>
</property>
<property name="mappings">
<props>
<prop key="/studentList.htm">
studentListController
</prop>
<prop key="/createStudent.htm">
studentCreateController
</prop>
</props>
</property>
</bean>

Internationalization and Localization

Spring supports locale handling in a easy way. It is pretty much similar to other framewroks and uses the properties file. Let's register the MessageSource from Spring in springapp-Servlet.xml

<bean id="messageSource" 
class="org.springframework.context.support.ResourceBundleMessageSource">
   <property name="basename">
     <value>messages</value>
   </property>
</bean>

This will look for properties file for a locale in the classpath with prefix messages. Each locale is resolved accordingly. For example the default locale file will be messages.properties and for german locale it will be messages_de_DE.properties. Spring checks the accepted locale as send by the client and does a close match. The locales can be set in all the browsers. It's also possible to change the default so that Spring looks for locale as a parameter name in request or gets the locale from session. Once this is done we can change our createStudent.jsp to pick the label name from locale. Let's write two properties file and put it at the root of source folder so that it comes in the classpath

messages.properties

name=Name
save=Save

messages_de_DE.properties

Name=Name
save=Au├čer

Now hit the application by putting the german locale on the top and than english locale on the top.

Validation

Spring provides validator mechanism which can be tied to controller. Let's write a validator which checks that the name of the student is not empty.
The validator class StudentValidator.java

public class StudentValidator implements Validator {

public boolean supports(Class clazz) {
return clazz.equals(Student.class);
}

// Actual validate method
public void validate(Object obj, Errors errors) {
//command object is also available
                Student student = (Student)obj;
//emptyField is resolved by looking into the properties file
//of locale. If it is not present than the fourth argument
//is used to display locale message     
                ValidationUtils.rejectIfEmptyOrWhitespace 
    (errors, "name", "emptyField","Field empty");
}
}

Register the validator with the controller

<bean id="studentCreateController" class="com.oyejava.springmvc.StudentCreateController">
   <property name="studentDao" ref="studentDao" />
   <property name="formView" value="createStudent" />
   <property name="commandName" value="student" />
   <property name="commandClass" value="com.oyejava.springmvc.Student" />
  <property name="validator">
<bean class="com.oyejava.springmvc.StudentValidator" />
  </property>
</bean>

Change the createStudent.jsp to handle error messages:

<body>
   <spring:hasBindErrors name="student">
<c:forEach var="error" items="${errors.allErrors}">
          <spring:message code="${error.code}" text="${error.defaultMessage}" />
</c:forEach>
    </spring:hasBindErrors> 

<form:form commandName="student">
<label for="name"><spring:message code="name" /></label>
<form:input path="name" />
<input type="submit" value=<spring:message code="save"/> />
</form:form>
</body>

Please see the following videos which cover the concepts:


No comments:

Post a Comment