Spring MVC Blueprints

4.5 (2 reviews total)
By Sherwin John Calleja Tragura
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Creating a Personal Web Portal (PWP)

About this book

Spring MVC is the ideal tool to build modern web applications on the server side. With the arrival of Spring Boot, developers can really focus on the code and deliver great value, leveraging the rich Spring ecosystem with minimal configuration.

Spring makes it simple to create RESTful applications, interact with social services, communicate with modern databases, secure your system, and make your code modular and easy to test. It is also easy to deploy the result on different cloud providers.

This book starts all the necessary topics in starting a Spring MVC-based application. Moving ahead it explains how to design model objects to handle file objects. save files into a data store and how Spring MVC behaves when an application deals with uploading and downloading files. Further it highlights form transactions and the user of Validation Framework as the tool in validating data input. It shows how to create a customer feedback system which does not require a username or password to log in. It will show you the soft side of Spring MVC where layout and presentation are given importance. Later it will discuss how to use Spring Web Flow on top of Spring MVC to create better web applications. Moving ahead, it will teach you how create an Invoice Module that receives and transport data using Web Services

By the end of the book you will be able to create efficient and flexible real-time web applications using all the frameworks in Spring MVC.

Publication date:
July 2016
Publisher
Packt
Pages
490
ISBN
9781785888274

 

Chapter 1.  Creating a Personal Web Portal (PWP)

This chapter is all about creating a robust and simple personal web portal that can serve as a personal web page, or a professional reference site, for anyone. Usually, these kinds of websites are used as mashups, or dashboards, of centralized sources of information describing an individual or group.

Technically, a personal web portal is a composition of web components like CSS, HTML, and JavaScript, woven together to create a formal, simple or exquisite presentation of any content. It can be used, in its simplest form, as a personal portfolio or an enterprise form like an e-commerce content management system. Commercially, these portals are drafted and designed using the principles of the Rich-client platform or responsive web designs. In the industry, most companies suggest that clients try easy-to-use-tools like PHP frameworks (for example, CodeIgniter, Laravel, Drupal) and seldom advise using JEE-based portals.

Aside from the software processes and techniques that will be discussed in this chapter, the main goal is for the reader to have a quick but detailed review of the main recipe of Spring MVC 4.x implementation, and to know the importance of Java Enterprise Edition (JEE) concepts behind any Java Enterprise frameworks.

In this chapter, you will learn how to:

  • Implement a complete Spring MVC framework

  • Configure DispatcherServlet in a Spring MVC project

  • Learn types of controllers and their current features

  • Use controller annotations

  • Map URLs to controllers

  • Use different types of models in dispatching objects

  • Validate form domain objects using Validator

  • Convert and transform request parameter values into other object types

  • Configure views

  • Configure and implement e-mail transactions

  • Deploy Spring MVC projects

 

Overview of the project


The personal web portal (PWP) created publishes a simple biography, and professional information, one can at least share through the Web. The prototype is a session-driven one that can do dynamic transactions, like updating information on the web pages, and posting notes on the page without using any back-end database.

Through using wireframes, below are the initial drafts and design of the web portal:

  • The Home Page: This is the first page of the site that shows updatable quotes, and inspiring messages coming from the owner of the portal. It contains a sticky-note feature at the side that allows visitors to post their short greetings to the owner in real-time.

  • The Personal Information Page: This page highlights personal information of the owner including the owner's name, age, hobbies, birth date, and age. This page contains part of the blogger's educational history. The content is dynamic and can be updated at any time by the owner.

  • The Professional Information Page: This page presents details about the owner's career background. It lists down all the previous jobs of the account owner, and enumerates all skills-related information. This content is also updatable.

  • The Reach Out Page: This serves as the contact information page of the owner. Moreover, it allows visitors to send their contact information, and specifically their electronic mail address, to the portal owner.

  • Update pages: The Home, Personal and Professional pages have updateable pages for the owner to update the content of the portal. The prototype has the capability to update the information presented in the content at any time the user desires.

This simple prototype, called PWP, will give clear steps on how to build personal sites, from the ground up, using Spring MVC 4.x specifications. It will give enthusiasts the opportunity to start creating Spring-based web portals in just a day, without using any database backend. To those who are new to the Spring MVC 4.x concept, this chapter will be a good start in building full-blown portal sites.

 

Technical requirements


In order to start the development, the following tools need to be installed onto the platform:

  • Java Development Kit (JDK) 1.7.x

  • Spring Tool Suite (Eclipse) 3.6

  • Maven 3.x

  • Spring Framework 4.1

  • Apache Tomcat 7.x

  • Any operating system

First, the JDK 1.7.x installer must be installed. Visit the site http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html to download the installer.

Next, set up the Spring Tool Suite 3.6 (Eclipse-based) which will be the official Integrated Development Environment (IDE) of this book. Download the Spring Tool Suite 3.6 at https://spring.io/tools/sts.

 

Setting-up the development environment


This book recommends the Spring Tool Suite (Eclipse) 3.6 since it has all the Spring Framework 4.x plug-ins, and other dependencies needed by the projects. To start us off, the following image shows the dashboard of the STS IDE:

Conversely, Apache Maven 3.x will be used to build and deploy the project for this chapter. Apache Maven is a software project management and comprehension tool. Based on the concept of a project object model (POM), Maven can manage a project's build, reporting and documentation from a central piece of information (https://maven.apache.org/).

There is already a Maven plugin installed in the STS IDE that can be used to generate the needed development directory structure. Among the many ways to create Spring MVC projects, this chapter focuses on two styles, namely:

  • Converting a dynamic web project to a Maven specimen

  • Creating a Maven project from scratch

Converting a dynamic web project to a Maven project

To start creating the project, press CTRL + N to browse the menu wizard of the IDE. This menu wizard contains all the types of project modules you'll need to start a project. The menu wizard should look similar to the following screenshot:

Once on the menu, browse the Web option and choose Dynamic Web Project. Afterwards, just follow the series of instructions to create the chosen project module until you reached the last menu wizard, which looks like the following figure:

This last instruction (Web Module panel) will auto-generate the deployment descriptor (web.xml) of the project. Always click on the Generate web-xml deployment descriptor checkbox option. The deployment descriptor is an XML file that must reside inside the /WEB-INF/ folder of all JEE projects. This file describes how a component, module or application can be deployed. A JEE project must always be in the web.xml file otherwise the project will be defective.

Note

Since the Spring 4.x container supports the Servlet Specification 3.0 in Tomcat 7 and above, web.xml is no longer mandatory and can be replaced by org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer or  org.springframework.web.servlet.support.AbstractDispatcherServletInitializer class.

The next major step is to convert the newly created dynamic web project to a Maven one. To complete the conversion, right-click on the project and navigate to the Configure | Convert Maven Project command set, as shown in the following image:

It is always best for the developer to study the directory structure of the project folder created before the actual implementation starts. The following is the directory structure of the Maven project after the conversion:

The project directories are just like the usual Eclipse dynamic web project without the pom.xml file.

Creating a Maven project from scratch

Another method of creating a Spring MVC web project is by creating a Maven project from the start. Be sure to install the Maven 3.2 plugin in STS Eclipse. Browse the menu wizard again, and locate the Maven option. Click on the Maven Project to generate a new Maven project.

After clicking this option, a wizard will pop up, asking if an archetype is needed or not to create the Maven project. An archetype is a Maven plugin whose main objective is to create a project structure as per its template. To start quickly, choose an archetype plugin to create a simple Java application here. It is recommended to create the project using the archetype maven-archetype-webapp. However, skipping the archetype selection can still be a valid option.

After you've done this, proceed with the Select an Archetype window shown in the following screenshot. Locate maven-archetype-webapp then proceed with the last process.

The selection of the Archetype maven-archetype-webapp will require the input of Maven parameters before ending the whole process with a new Maven project:

The required parameters for the Maven group or project are as follows:

  • Group ID (groupId): This is the ID of the project's group and must be unique among all the project's groups.

  • Artifact ID (artifactId): This is the ID of the project. This is generally the name of the project.

  • Version (version): This is the version of the project.

  • Package (package): The initial or core package of the sources.

For more information on Maven plugin and configuration details, visit the documentation and samples on the site http://maven.apache.org/.

After providing the Maven parameters, the project source folder structure will be similar to the following screenshot:

 

The pom.xml file


The basic fundamental unit of work in Maven is the pom.xml file. This XML file is the main part of the Maven project folder structure, and is always located in the base directory of the project. The file contains all the necessary archetype plugins and dependencies for project building and deployment.

The PWP has this pom.xml file that builds sources, creates project WAR files, and deploys the projects with web.xml to the Tomcat server.

<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/xsd/maven-4.0.0.xsd"> 
  <modelVersion>4.0.0</modelVersion> 
  <groupId>org.packt.spring.codes</groupId> 
  <artifactId>ChapterOne</artifactId> 
  <version>0.0.1-SNAPSHOT</version> 
  <packaging>war</packaging> 
  <name>ChapterOne</name> 
  <description>Personal Web Portal</description> 
 
  <!-- properties --> 
  <properties> 
    <spring.version>4.1.2.RELEASE</spring.version> 
    <servlet.api.version>3.1.0</servlet.api.version> 
  </properties> 
  <!-- dependencies --> 
 
  <dependencies> 
    <dependency> 
    <groupId>javax.mail</groupId> 
    <artifactId>mail</artifactId> 
    <version>1.4.3</version> 
  </dependency> 
 
  <dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-core</artifactId> 
    <version>${spring.version}</version> 
  </dependency> 
 
  <dependency> 
    <groupId>org.springframework</groupId> 
    <artifactId>spring-beans</artifactId> 
    <version>${spring.version}</version> 
  </dependency> 
 
  <!-- Rest of the dependencies in sources --> 
  </dependencies> 
 
  <build> 
    <finalName>spring-mvc-maven-webapp-from-scratch</finalName> 
      <plugins> 
        <!-- Apache Tomcat 7 Maven Plugin --> 
        <-- 
        <plugin> 
          <groupId>org.apache.tomcat.maven</groupId> 
          <artifactId>tomcat7-maven-plugin</artifactId> 
          <version>2.2</version> 
          <configuration> 
 
            <url>http://localhost:8080/manager/text</url> 
            <path>/ch01</path> 
            <username>admin</username> 
            <password>admin</password> 
          </configuration> 
        </plugin> 
        --> 
        <plugin> 
          <groupId>org.apache.maven.plugins</groupId> 
          <artifactId>maven-compiler-plugin</artifactId> 
          <version>3.1</version> 
          <configuration> 
            <source>1.7</source> 
            <target>1.7</target> 
          </configuration> 
        </plugin> 
        <!-- Mojo Maven Plugin --> 
          <plugin> 
            <groupId>org.codehaus.mojo</groupId> 
            <artifactId>tomcat-maven-plugin</artifactId> 
            <version>1.1</version> 
            <configuration> 
              <url>http://localhost:8080/manager/text</url> 
              <server>TomcatServer</server> 
              <path>/ch01</path> 
            </configuration> 
          </plugin> 
        </plugins> 
    </build> 
  </project> 

After setting the development and deployment environment, it is time to start configuring out the Spring MVC component of PWP.

 

Project deployment


Apache Tomcat 7 will be used as the application server for this chapter. There are two popular Maven plugins that can be used to deploy applications to Tomcat: the Apache Maven plugin and the Mojo Maven plugin. By default, this book uses the Mojo Maven plugin.

To deploy a Maven project to Tomcat using the Mojo Maven plugin, the following configuration must be followed:

  1. Locate the user/conf/tomcat-users.xml file on the Tomcat server and add the desired user with administrator rights. Information like role, username and password of the added user must be added inside the <tomcat-user> tag. A sample configuration is shown in the following code snippet:

          <role rolename="manager"/> 
          <role rolename="admin"/> 
          <user username="admin" password="admin" 
          roles="admin,manager "/> 
    
    • Now save the file.

  2. Locate the file ~/.m2/settings.xml in Maven. If the file is non-existent, download Maven from its site and copy the settings.xml to ~/.m2. Open the file and locate the <servers> tag. Insert the following tag inside the <servers> tag.

            <server> 
              <id>TomcatServer</id> 
              <username>admin</username> 
              <password>admin</password> 
            </server> 
    
    • The <id> is any desired name for the Tomcat server that will be later called by the pom.xml plugin for Tomcat 7. The <username> and <password> are the server credentials manually added in /conf/tomcat-users.xml above. Save the file.

  3. Open pom.xml and add the following Maven plugin for Tomcat 7 server.

            <plugin> 
              <groupId>org.codehaus.mojo</groupId> 
              <artifactId>tomcat-maven-plugin</artifactId> 
              <version>1.1</version> 
              <configuration> 
                <url>http://localhost:8080/manager/text</url> 
                <server>TomcatServer</server> 
                <path>/ch01</path> 
              </configuration> 
            </plugin> 
    

The <configuration> settings contain information of where to deploy the project (<url>), what context root to use (<path>/), and the server name (<server>) used in the settings.xml file.

To deploy using the Apache Tomcat7 Maven plugin, the <configuration> tag only needs the <username> and <password> tags of the administration console, including the <url> and the <path>.

Maven deployment process

All projects are deployed in the Tomcat 7 server using the Maven plugin installed in STS. Follow the steps below to properly deploy the Spring MVC projects.

  1. Be sure to have a correctly configured Maven project with the appropriate POM configuration shown in the preceding figure.

  2. Right-click on the project and locate Run As in the menu options. Then, locate Maven build... in the sub-menu option, to configure the Maven goals for the first time. Maven goal is a task or command that is executed to build and manage Maven projects.

  3. After this, a Maven configuration panel will pop up to write the needed goals for deployment, and to launch the deployment process. If the Mojo Maven plugin is used, write on the Goals textbox the following: clean install tomcat:deploy. If the Apache Tomcat7 Maven plugin is used, the goals must be: clean install tomcat7:deploy. Aside from these goals, there are some that can be essential for management like remove, update and re-deploy.

  4. Click the Run button to launch the deployment. All the Maven execution logs will be shown on the console. The outcome of the deployment will be either a BUILD SUCCESS or a BUILD FAILURE.

 

Project libraries and dependencies


Configure pom.xml to add the major Spring Framework 4.x libraries (JAR files) for the PWP project. These dependency modules are the following:

  • spring-core (Spring core module): This contains the core components of the framework which includes the Inverse of Control principle and Dependency Injection (DI).

  • spring-beans (Spring bean module): This contains the bean generation using BeanFactory and fetches injected beans using the method getBean().

  • spring-context (Spring context module): Built by the core and bean modules that provide the interfaces of ApplicationContext with some features like resource bundling, internationalization, and scheduling.

  • spring-context-support (Spring context support module): This module contains the classes needed for integrating third-party applications to a Spring Application Context.

  • spring-web: This contains the web features of the Spring Framework which includes the initialization of the IoC container using servlet listeners and a web-oriented application context.

  • spring-webmvc (Spring MVC module): This is the module that has the MVC implementations and features.

  • spring-tx (Spring Transaction module): This contains transaction management on Bean object declarations with some special interfaces for all the POJO objects.

Aside from the other non-framework libraries, the following are auxiliary JAR files that support the Spring Framework 4.x core libraries:

  • servlet-api: This contains all the classes and interfaces that describe the interaction between a servlet class and the runtime environment provided for the instance of a class within the bound of the servlet container.

  • jsp-api: This contains all the classes that implement the JspPage interface.

  • jstl: This contains all the classes and interfaces for Taglib support for all JSP pages.

  • javax-mail: This provides a platform-independent and protocol-independent framework to build mail and messaging applications.

  • javax.validation: This provides JSR-303 annotations for Java Bean validation.

All of these dependencies must be added to the pom.xml of the Maven project.

 

Overview of the Spring MVC specification


The Spring MVC framework derives its specification from the Model-View-Controller (MVC) design pattern that separates the application into layers such as business, logic, navigation and presentation. The principle behind this design pattern is to create a de-coupled or loosely-coupled architecture, which is more flexible than the tightly-coupled frameworks.

Technically, Spring MVC works starts with a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale, time zone and theme resolution, as well as support for uploading files. The default handler is based on the @Controller and @RequestMapping annotations, offering a wide range of flexible handling methods. With the introduction of Spring 3.0, the @Controller mechanism also allows you to create RESTful Web sites and applications, through the @PathVariable annotation and other features (http://docs.spring.io/).

The following diagram depicts how DispatcherServlet manages the whole MVC framework while, at the same time, avoiding the Fat Controller syndrome.

The org.springframework.web.servlet.DispatcherServlet is an actual servlet in the web.xml file of your web application, declared using the standard servlet tags. Just like any typical servlets, it recognizes request transactions through URL mappings. This servlet serves as the front controller of the whole MVC project.

Since this PWP project is written using the Spring Framework 4.x specification, the implementations always starts by declaring the DispatcherServlet.

 

The project development


All the nuts and bolts involved in developing the PWP from scratch will be discussed in this topic. Each of the four web pages, including their internal processes, will be scrutinized using the codes of the project. The Spring MVC concepts will focus on the following areas:

  • Configuration of DispatcherServlet

  • Configuration of Spring container

  • Creating controllers

  • Types of attributes

  • Validation

  • Type conversion and transformation

  • E-mail support configuration

  • Views and ViewResolvers

Configuring the DispatcherServlet

We start creating the Spring MVC project by configuring the DispatcherServlet API class. The Spring MVC framework has the DispatcherServlet at the center of all request and response transactions as illustrated in the preceding figure.

From the point of view of the PWP, the DispatcherServlet starts receiving requests when the user starts running pages on the web browser. The processes are enumerated as follows:

  • When the container receives a request from a path, the DispatcherServlet checks whose controller is mapped to the path name.

  • Then, the controller acknowledges the request with the appropriate service methods (for example, GET, POST, PUT, HEAD), executes the appropriate transaction method with the given model(s), and then returns the view name to the DispatcherServlet.

  • Then, the DispatcherServlet checks which type of view resolver has been configured from its container. Through the view resolver, the DispatcherServlet will know the appropriate view that matches the given request.

  • Finally, the DispatcherServlet will process the transport of model data to the view for presentation or rendition.

The PWP has the following configuration for the DispatcherServlet:

<?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_3_0.xsd"    id="WebApp_ID"  
  version="3.0"> 
  <display-name>ChapterOne</display-name> 
 
  <!-- Declare Spring DispatcherServlet --> 
  <servlet> 
    <servlet-name>pwp</servlet-name> 
    <servlet-class> 
      org.springframework.web.servlet.DispatcherServlet 
    </servlet-class> 
  </servlet> 
  <servlet-mapping> 
    <servlet-name>pwp</servlet-name> 
    <url-pattern>*.html</url-pattern> 
  </servlet-mapping> 
 
  <!-- Spring accepted extension declared here below --> 
  <mime-mapping> 
    <extension>png</extension> 
    <mime-type>image/png</mime-type> 
  </mime-mapping> 
 
</web-app> 

Just like any typical JEE servlet, the tags <servlet> and <servlet-mapping> are used to declare the dispatcher servlet DispatcherServlet:

<servlet> 
  <servlet-name>pwp</servlet-name> 
  <servlet-class> 
    org.springframework.web.servlet.DispatcherServlet 
  </servlet-class> 
</servlet> 
<servlet-mapping> 
  <servlet-name>pwp</servlet-name> 
  <url-pattern>*.html</url-pattern> 
</servlet-mapping> 

The <servlet-name> tag does not only stand for the name of the servlet, but is also related to the name of a Spring container which will be tackled later. The  <url-pattern> indicates which type of valid URLs will be recognized by the DispatcherServlet during request-response transactions. In our preceding configuration, it shows that all URL must have an extension .html in order for the requests to be processed by the servlet.

When it comes to file types, the DispatcherServlet only considers content types declared with the <mime-mapping> tag. In this project, we only have PNG files needed by the portal.

  <mime-mapping> 
    <extension>png</extension> 
    <mime-type>image/png</mime-type> 
  </mime-mapping> 
 

Creating the Spring container


After we've configured the DispatcherServlet, the Spring MVC container must be created. The interface org.springframework.context.ApplicationContext is Spring's more advanced container. The implementation of this object manages other objects of the portal application that will be known later as beans. All beans are injected into this container so that the portal will just "fetch" them once they are needed in several transactions.

There are two ways to create a Spring MVC container and these are:

  • XML-based configuration

  • JavaConfig-based configuration

Spring container configuration using XML

Using our STS IDE, the ApplicationContext (applicationContext.xml) can be created using the Spring Eclipse plugin. Following is the menu wizard showing the plugin for the Spring Framework module:

On the wizard, click on the Spring Bean Configuration File option which will guide you to the next instruction panel. This is the selection of the XSD namespaces needed by the applicationContext.xml for the Spring components.

After choosing the necessary XSD namespaces needed for bean injections and configurations, the PWP's XML-based container will look like the following configuration:

<?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:aop="http://www.springframework.org/schema/aop" 
  xmlns:context="http://www.springframework.org/schema/context" 
  xmlns:oxm="http://www.springframework.org/schema/oxm"
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  xsi:schemaLocation="http://www.springframework.org/schema/aop    http://www.springframework.org/schema/aop/spring-aop-4.1.xsd 
http://www.springframework.org/schema/oxm      http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd 
http://www.springframework.org/schema/beans      http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
http://www.springframework.org/schema/tx      http://www.springframework.org/schema/tx/spring-tx-4.1.xsd 
http://www.springframework.org/schema/context      http://www.springframework.org/schema/context/spring-context-4.1.xsd 
http://www.springframework.org/schema/mvc      http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd"> 
 
    // all beans injected here 
</beans> 

The applicationContext.xml is loaded, accessed, and read by the DispatcherServlet. The convention used so the DispatcherServlet recognizes the container is to name our applicationContext.xml file using the format dispatcherServletName-servlet.xml, wherein the dispatcherServletName is the name indicated by the <servlet-name> tag:

<servlet> 
  <servlet-name>pwp</servlet-name> 
  <servlet-class> 
    org.springframework.web.servlet.DispatcherServlet 
  </servlet-class> 
</servlet> 

Thus, the PWP's applicationContext.xml file is named pwp-servlet.xml. The default filename of an XML-based Spring container must always follow the convention [dispatcher-servlet-name]-servlet.xml.

Spring container configuration using JavaConfig

The other type of implementation of the Spring MVC container is through the JavaConfig classes. In the JavaConfig method, every component tag has its respective annotation equivalent. Following is the equivalent configuration of the preceding XML-based setup.

package org.packt.personal.web.portal.config; 
import org.springframework.context.annotation.Configuration; 
 
@Configuration 
public class PersonalWebPortalConfig {   } 

The @Configuration annotation indicates that this class contains one or more bean methods (usually getters) annotated with @Bean that returns manageable beans of the container. Some references call this class a configurator class.

The two implementations are incomparable, but some projects mix them. XML-based Spring MVC containers become cumbersome when the files get larger, while annotation-based ones can be managed since they are just POJO-based. JavaConfig is a better approach when it comes to using a rapid application development (RAD) strategy. The reason for this is that when the number of components in the project increases, JavaConfig can only just manage the dependencies among beans through autowiring, which the XML cannot impose since the codes are decoupled from the dependency injection process. Debugging is also easy, since bug detection will be done during compilation, unlike in the XML-style where errors will be detected right after the deployment or execution of the application.

On the issue of mixing them, the project must choose which configuration is going to be bootstrapped by the container. If it is the JavaConfig, it must use the @ImportResource to load all the injected beans from the XML:

@Configuration 
@ImportResource("classpath:pws-servlet.xml") 
public class PersonalWebPortalConfig {   } 

If the XML-based configuration is used instead, the XML must use <context:component-scan="org.packt.personal.web.portal"/> to locate the @Configuration class and load all @Bean autowired in it.

In general, the XML-based method is preferred, whenever an Enterprise application is being developed, because XML is still widely used in systems integration techniques. Some legacy systems also preferred the XML-based container.

The PWP project has an XML-based and JavaConfig-based container.

 

Configuring the Spring container


Before we create controllers for our PWP project, the Spring container must be ready for bean injections and component declarations. First, our Spring MVC container must be annotation-driven so that we can utilize the annotation stereotypes used by the current Spring Framework specification in configuring containers.

To enable the use of annotations inside classes, the following tag must be appearing in the pwp-servlet.xml:

    <mvc:annotation-driven /> 

Second, when the <annotation-driven> tag is enabled, the container must automatically scan all component classes that are part of the Spring MVC web project. This will be enabled through inserting the following tag into the pwp-servlet.xml.

    <context:component-scan  
      base-package="org.packt.personal.web.portal" /> 

The base-package attribute indicates the base folder of the development directory structure (src) where all components and beans are located. Declaring this tag enables auto-detection of the bean components in the project, which includes the controllers.

Drop all static resources like the CSS files into the ch01/webapp folder. Declare the default servlet handler in order to allow the access of those static resources from the root of the web application even though the DispatcherServlet is registered at the context root ch01.

    <mvc:default-servlet-handler /> 

Then, use the <mvc:resources> element to point to the location of the static resources with a specific public URL pattern.

    <mvc:resources mapping="/css/**" location="/css/" /> 

The complete XML-based Spring container of PWP is shown as follows:

<?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:aop="http://www.springframework.org/schema/aop" 
  xmlns:context="http://www.springframework.org/schema/context" 
  xmlns:oxm="http://www.springframework.org/schema/oxm" 
  xmlns:tx="http://www.springframework.org/schema/tx" 
  xmlns:mvc="http://www.springframework.org/schema/mvc" 
  xsi:schemaLocation="http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop-4.1.xsd 
    http://www.springframework.org/schema/oxm
    http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd 
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/
         spring-beans-4.1.xsd 
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.1.xsd 
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/
         spring-context-4.1.xsd 
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd"> 
 
  <mvc:annotation-driven /> 
  <mvc:default-servlet-handler /> 
  <mvc:resources mapping="/css/**" location="/css/" /> 
</beans> 

The JavaConfig equivalent of our XML-based container will have this equivalent code:

import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
// Rest of the imports in sources 
 
@Configuration 
@ComponentScan(basePackages="org.packt.personal.web.portal") 
@EnableWebMvc 
public class PWPConfiguration extends WebMvcConfigurerAdapter { 
 
  @Override 
    public void addResourceHandlers(ResourceHandlerRegistry      registry) { 
      registry.addResourceHandler("/css/**").       addResourceLocations("/css/"); 
    } 
} 

The @EnableWebMvc annotation is equivalent to <mvc:annotation-driven /> in the XML-based version . Lastly, the @ComponentScan annotation is equivalent to the <context:component-scan base-package=" org.packt.personal.web.portal "/> we have in our pwp-servlet.xml. To allow recognition to static resources, the application must override the resource handler method.

Creating the controllers

The PWP project will consist of four content pages each having its own use request transaction. Users can update three of the four contents using session handling. In order to implement services to each page, controller class must be created with the stereotype @Controller instead of extending controller base classes or Spring-specific APIs. The home page of PWP has the following controller declaration:

@Controller 
public class IndexController {  } 

Each controller class has more than service or handler methods. Each service method is being mapped to the user's request by the DispatcherServlet. A valid service or handler method must have a @RequestMapping stereotype written on top of the method or function signature. A sample controller with a service method is shown as follows:

@Controller 
public class IndexController { 
 
  @RequestMapping(value="/index", method=RequestMethod.GET) 
  public String getIndex(Model model) { 
    model.addAttribute("greetings",  "Welcome Page"); 
    return "index"; 
  } 
} 

A service method is mapped to at least one URL or path, and has an HTTP method declared. The preceding sample getIndex() is mapped to the /index.html and processes a GET request transaction.

There are actually four general classifications of a controller and these are the following:

  • A controller that calls a page only: These controllers simply provide services that call view names like the following sample:

        @Controller 
          public class IndexController { 
 
            @RequestMapping(value="/index", 
            method=RequestMethod.GET) 
            public String getIndex() { 
              return "index"; 
            } 
          } 
  • A controller that brings model(s) to views: This group of controllers transport models to the view for presentation purposes. A controller can pass an org.springframework.web.ModelAndView to successful view pages with an object containing all the necessary objects, like in the following snippet:

        @Controller 
          public class IndexController { 
 
            @RequestMapping(value="/index", 
            method=RequestMethod.GET) 
            public ModelAndView getIndex() { 
              Map<String, Object> model = new HashMap<>(); 
              model.put("greetings",  "Welcome Page"); 
              return new ModelAndView("index", "model", model); 
            } 
 
          } 
  • On the other hand, the most modern and simple way of transporting model data is through the use of the org.springframework.ui.Model object, wherein all objects transported to the views are either @ModelAttribute or @SessionAttribute. A simple sample on this approach is given as follows:

        @Controller 
          public class IndexController { 
 
            @RequestMapping(value="/index", 
            method=RequestMethod.GET) 
            public String getIndex(Model model) { 
              model.addAttribute("greetings",  "Welcome Page"); 
              return "index"; 
            } 
          } 
  • A controller that has multi-services or multi-actions: Controllers that have more than one service or action:

        @Controller 
        public class IndexController { 
 
          @RequestMapping(value="/index", 
          method=RequestMethod.GET) 
          public String getIndex(Model model){ 
            model.addAttribute("greetings",  "Welcome Page"); 
            return "index"; 
          } 
 
          @RequestMapping(value="/index_post", 
            method=RequestMethod.POST) 
            public String postIndex(Model model) { 
              model.addAttribute("greetings",  "Welcome Page"); 
              return "index"; 
            } 
        } 
  • A controller that accepts request parameters (form controllers): These controllers have a form view and a success view. The form view accepts request parameters from the client. Then the request will be mapped to the service method, which will process all the request data. The result will be transported as a model, or as an attribute, to the corresponding view. Following is a sample snippet:

         @Controller 
         @SessionAttributes(value={"statusSess", "homeSess"}) 
         @RequestMapping("/pwp/index_update") 
         public class IndexUpdateController { 
 
         @Autowired 
         private Validator indexValidator; 
 
         @InitBinder("homeForm") 
         public void initBinder(WebDataBinder binder) { 
         binder.setValidator(indexValidator); 
         } 
 
         @RequestMapping(method=RequestMethod.GET) 
 
         public String initForm(Model model) { 
         Home homeForm = new Home(); 
         model.addAttribute("homeForm", homeForm); 
         return "index_update"; 
         } 
 
         @RequestMapping(method=RequestMethod.POST) 
         public String submitForm(Model model, 
         @ModelAttribute("homeForm") 
         @Validated Home homeForm, BindingResult binding) { 
         model.addAttribute("homeForm", homeForm); 
         String returnVal = "index"; 
         if(binding.hasErrors()) { 
         returnVal = "index_update"; 
         } else { 
         model.addAttribute("homeSess", homeForm); 
         model.addAttribute("statusSess", "undefault"); 
         } 
         return returnVal; 
         } 
         }

The PWP controllers

This project utilizes all the preceding controller classification except for multi-action controllers. Following is the finished product of the PWP that highlights the home page. All content pages have an equivalent update transaction for updating their content. The home page, in particular, is invoked through the URL http://localhost:8080/ch01/pwp/index.html where ch01 is the context root. With this URL, DispatcherServlet will look for the controller that has the service method bearing the path /pwp/index. Once the match is found, the service method calls the appropriate view with the data model, if there is one. The final step will be loading the content desired by the user.

A complete home page controller class with the default values of the cone is shown as follows:

package org.packt.personal.web.portal.controller; 
 
import java.util.ArrayList; 
import java.util.Date; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
 
import org.packt.personal.web.portal.model.form.Home; 
import org.packt.personal.web.portal.model.form.PostMessage; 
// Rest of the code in the sources 
 
@Controller 
@SessionAttributes("posts") 
@RequestMapping("pwp") 
public class IndexController { 
 
  @ModelAttribute("posts") 
  public List<PostMessage> newPosts() { 
    return initPost(); 
  } 
 
  @RequestMapping(value="/index", method=RequestMethod.GET) 
  public String getIndex(Model model,  @ModelAttribute("posts") 
    List<PostMessage> posts, 
    @ModelAttribute("postForm") PostMessage postForm) { 
 
      Home home = new Home(); 
      home.setMessage(initMessage()); 
      home.setQuote(initQuote()); 
      model.addAttribute("home",  home); 
        if(posts == null) posts = newPosts(); 
        if(validatePost(postForm)) { 
          postForm.setDatePosted(new Date()); 
          posts.add(postForm); 
        } 
        model.addAttribute("posts",  posts); 
      return "index"; 
  } 
 
  @RequestMapping(value="/index_redirect", 
    method=RequestMethod.GET) 
  public RedirectView updateIndex() { 
    return new RedirectView("/ch01/pwp/index_update.html"); 
  } 
 
  public String initQuote() { 
    String message = "Twenty years from now you will be more 
      disappointed by the things .......... -Mark Twain"; 
    return message; 
  } 
 
  public String initMessage() { 
    String message = "Having a positive outlook on life is a      crucial part of finding inspiration. In the paragraph above,      ......"; 
    return message; 
  } 
 
  public List<PostMessage> initPost() { 
    List<PostMessage> posts = new ArrayList<>(); 
    PostMessage post = new PostMessage(); 
    post.setSubject("Welcome!"); 
    post.setDatePosted(new Date()); 
    post.setPostedMsg("Hello visitors!Feelfree to post on my 
    portal!"); 
 
    posts.add(post); 
    return posts; 
  } 
 
  public boolean validatePost(PostMessage post) { 
    try { 
      if(post.getSubject().trim().length() == 0 || 
      post.getPostedMsg().trim().length() == 0) { 
        return false; 
      } 
    } 
    catch(Exception e) { 
      return false; 
    } 
    return true; 
  } 
} 

The method getIndex() is responsible for loading the home page initially. The default content for message and content are given by two methods, namely initMessage() and initPost(). The initPost() method initially populates the right navigation panel of the home page where all the posts will be listed. This posting feature will be elaborated on further in the next topics.

Generally, all these default methods are essential to generate the home page when no session data are available to replace this default content.

The update page on the home page will be invoked by clicking the Update button. Each portal has an Update button except for the Reach Out page. After clicking the Update button on the home page, DispatcherServlet will look for the service that has the URL path /pwp/index_redirect. There are two general ways to implement redirection and these are the following:

  • Using the RedirectView API: This process of redirection is done through implementation of the handler method that returns org.springframework.web.servlet.view.RedirectView. Following are some snippets that show how to implement redirection.

        @RequestMapping(value="/index_redirect",
                        method=RequestMethod.GET) 
          public RedirectView updateIndex() { 
            return new RedirectView("/ch01/pwp/index_update.html"); 
          } 
 
          @RequestMapping(value="/index_redirect",
                          method=RequestMethod.GET) 
            public String updateIndex() { 
              return (redirect:"/ch01/pwp/index_update.html"); 
            } 
  • Using the <c:url> JSTL tag: This process is a direct process of redirection and being configured on the view page.

            <c:url var=indexUpdate" value="/pwp/index_update.html"/> 
            <a href="<c:out value="${indexUpdate }"/>">Update Home</a>  
    

After redirection to the update page, the home page has two important pieces of content information that need to be updated, and these are the message and quote portions of the page:

All update pages have validators to check the attributes of the form data. On the home page, users cannot input quotes or messages that are less than 50 and more than 500 alphanumeric characters. Validation and object type checking occurs only if our controllers have form views. A controller that has form and success views is implemented in a different way. Following is a template of a form controller that is used by the PWP project:

package org.packt.personal.web.portal.controller; 
 
import java.util.ArrayList; 
import java.util.Date; 
import java.util.List; 
 
import org.packt.personal.web.portal.converter.AgeConverter; 
import org.packt.personal.web.portal.converter.BirthDateConverter; 
// Rest of the codes in the sources 
@Controller 
@SessionAttributes(value = {"pStatusSess", "personSess"}) 
@RequestMapping("/pwp/personal_update") 
public class PersonalUpdateController { 
 
  @Autowired 
  Validator personalValidator; 
 
  @InitBinder("personForm") 
  public void initBinder(WebDataBinder binder) { 
    binder.setValidator(personalValidator); 
    binder.registerCustomEditor(Integer.class, "biography.age", 
    new AgeConverter()); 
    binder.registerCustomEditor(Integer.class, "education.year", 
    new YearConverter()); 
    binder.registerCustomEditor(Date.class, "biography.birthDate", 
    new BirthDateConverter()); 
  } 
 
  @RequestMapping(method=RequestMethod.GET) 
  public String initForm(Model model) { 
    Biography bio = new Biography(); 
    Education educ = new Education(); 
    Personal personForm = new Personal(); 
    personForm.setBiography(bio); 
    personForm.setEducation(educ); 
    references(model); 
    model.addAttribute("personForm", personForm); 
    return "personal_update"; 
  } 
 
  @RequestMapping(method=RequestMethod.POST) 
  public String submitForm(Model model, 
  @ModelAttribute("personForm") @Validated Personal personForm, 
  BindingResult binding) { 
    model.addAttribute("personForm", personForm); 
    String returnVal = "personal"; 
    if(binding.hasErrors()) { 
      references(model); 
      returnVal = "personal_update"; 
    } else { 
      model.addAttribute("personSess", personForm); 
      model.addAttribute("pStatusSess", "undefault"); 
    } 
    return returnVal; 
 
  } 
 
  public void references(Model model) { 
    List<String> hobbiesList = new ArrayList<String>(); 
    hobbiesList.add("Not Applicable"); 
    hobbiesList.add("Singing"); 
    hobbiesList.add("Painting"); 
    hobbiesList.add("Traveling"); 
    hobbiesList.add("Writing"); 
    hobbiesList.add("Swimming"); 
    model.addAttribute("hobbiesList", hobbiesList); 
    // Rest of the code in sources 
  } 
} 

Although there is no restriction as to what, and how many, handler methods are needed in a typical controller class, a form controller has to maintain having only one @RequestMapping stereotype declared at the class level. The path indicated must be the controller's form view.

The preceding controller has two service operations namely the initForm() and submitForm() methods. The initForm() service is always a GET method, which always initializes the form view. It also binds the form domain object that contains the request data from the user. The submitForm() is a POST service that is executed after form submission. This automatically extracts the validated domain object from the form and transports any model to the result views.

The references() method is used to populate the form components found in the form view. Java Collection Framework (JCF) is used to contain all the data which can be hardcoded or which is from a data store.

The usual exception during form-handling happens when the form-backing object, or the model, is not set to bind with the form view. Whenever the browser calls the initForm(), the following exception will be encountered.

java.lang.IllegalStateException: 
  Neither BindingResult nor plain target object 
    for bean name 'home' available as request attribute 
      at o.s.w.s.s.BindStatus.<init>(BindStatus.java:141) 

In order for submitForm() to access the form-backing object, the @ModelAttribute annotation is needed to be declared in its parameters. An @ModelAttribute local parameter indicates that the argument will be retrieved from the form view. If not present in the form view, the argument must be instantiated first in the initForm(), and bound to the form view. Moreover, Spring MVC throws an exception when errors occur during request binding, by default. That is why in submitForm(), one of the arguments is for BindIngResult where we can extract all errors during the request transaction. The BindingResult argument needs to be positioned right after our form-backing object, which is a rare case when it comes to the @RequestMapping order of arguments.

If this rule is violated, the following exception will be thrown:

java.lang.IllegalStateException:
Errors/BindingResult argument declared without preceding model    attribute 

Using @ModelAttribute and @SessionAttributes

The Personal Web Portal (PWP) application does not have database connectivity since the main highlight in this chapter are the raw Spring MVC 4 components. Also, it has no Spring security attached to it from the core, so there are no login/logout transaction for the user. All controllers in the PWP use a lot of @ModelAttribute and @SessionAttributes data.

The @ModelAttribute data are actually the same with request attributes. These are objects created from one single request dispatching. Moreover, these data are also called request-scoped data, which means that these data values are only valid within one request-response transaction.

One use of @ModelAttribute is to bind the form domain object to the form view page. On an incoming request transaction, any models with @ModelAttributes are called before any controller handler method. The following initForm() method is one example of this scenario.

@RequestMapping(method=RequestMethod.GET) 
  public String initForm(Model model, 
  @ModelAttribute("profesionalForm") 
  Professional professionalForm) { 
    return "professional_update"; 
  } 
 
@RequestMapping(method=RequestMethod.GET) 
  public String initForm(Model model) { 
    Professional professionalForm = new Professional(); 
 
    model.addAttribute("professionalForm", professionalForm); 
    references(model); 
    return "professional_update"; 
  } 
  public void references(Model model) { 
    List<String> hobbiesList = new ArrayList<String>(); 
    hobbiesList.add("Not Applicable"); 
    hobbiesList.add("Singing"); 
    hobbiesList.add("Painting"); 
    hobbiesList.add("Traveling"); 
    hobbiesList.add("Writing"); 
    hobbiesList.add("Swimming"); 
    model.addAttribute("hobbiesList", hobbiesList); 
 
    List<String> readingsList = new ArrayList<String>(); 
    readingsList.add("Not Applicable"); 
    readingsList.add("Novel"); 
    readingsList.add("Magazine"); 
    readingsList.add("Newspaper"); 
    readingsList.add("Diaries"); 
    model.addAttribute("readingsList", readingsList); 
 
    List<String> educLevelList = new ArrayList<String>(); 
    educLevelList.add("Not Applicable"); 
    educLevelList.add("Doctoral"); 
    educLevelList.add("Masters"); 
    educLevelList.add("College"); 
    educLevelList.add("Vocational"); 
    educLevelList.add("High School"); 
    model.addAttribute("educLevelList", educLevelList); 
  } 

To initialize the @ModelAttribute, it must be injected with an object value just like we have in the PWP project:

@ModelAttribute("greetings") 
  public List<String> getGreetings() { 
    return Greetings.allGreets(); 
  } 

The @SessionAttributes, on the other hand, are used by the controller to declare session objects during session handling. Equally, these attributes are the session attributes in a typical JEE web component. As long as the session is not terminated, session data are always accessible for update and retrieval. Sessions were used in the PWP project to store values for all the dynamic content transactions. Here is a code snippet that shows how to declare, initialize and update values that are @SessionAttributes.

@Controller 
@SessionAttributes("posts") 
@RequestMapping("pwp") 
public class IndexController { 
 
  @ModelAttribute("posts") 
  public List<PostMessage> newPosts() { 
    return initPost(); 
  } 
 
  @RequestMapping(value="/index", method=RequestMethod.GET) 
  public String getIndex(Model model,  @ModelAttribute("posts") 
    List<PostMessage> posts, 
    @ModelAttribute("postForm") PostMessage postForm) { 
 
      Home home = new Home(); 
      home.setMessage(initMessage()); 
      home.setQuote(initQuote()); 
      model.addAttribute("home",  home); 
 
      if(posts == null) posts = newPosts(); 
      if(validatePost(postForm)) { 
        postForm.setDatePosted(new Date()); 
        posts.add(postForm); 
      } 
 
      model.addAttribute("posts",  posts); 
      return "index"; 
    } 
    // Other codes refer to sources 
} 

The @SessionAttributes are declared on the controller level and are initialized as with any @ModelAttribute. Always initialize the session data to avoid the following error:

Servlet.service() for servlet [pwp] in context with path [/ch01] threw exception [Expected session attribute 'posts'] with root cause
org.springframework.web.HttpSessionRequiredException: Expected session attribute 'posts'

In order for the service operations to access the session attribute argument, be sure to have a @ModelAttribute parameter on the local parameter of the service depicting the attribute data:

public String getIndex(Model model,  @ModelAttribute("posts") 
  List<PostMessage> posts, 
  @ModelAttribute("postForm") PostMessage postForm) { 
  } 

These two attributes trigger the information workflow of the PWP project, even without the help of any web services or data store.

Form domain objects

Binding to the form view using @ModelAttribute, forms domain objects that are plain POJOs, and are used to save request parameter values from the user. Sometimes these objects are also called form data models. See the following domain:

package org.packt.personal.web.portal.model.form; 
 
public class Home { 
 
  private String message; 
  private String quote; 
 
  public String getMessage() { 
    return message; 
  } 
 
  public void setMessage(String message) { 
    this.message = message; 
  } 
 
  public String getQuote() { 
    return quote; 
  } 
 
  public void setQuote(String quote) { 
    this.quote = quote; 
  } 
} 

Instantiation of this model will be found in the initForm() method and the binding to the form view page will be implemented using the Spring Form Taglib directive:

<%@ taglib prefix="form"  uri="http://www.springframework.org/tags/form"%> 

The Spring MVC <form> tag has its own form components that can be readily mapped to the setters of the form domain objects. These topics will be highlighted in Chapter 3, Student Management Portal (SMP), of this book. The following JSP code snippet shows how the binding is done through the <form> tag:

<form:form commandName="homeForm" method="post"> 
  <div class="form_settings"> 
    <!-- insert the page content here --> 
    <h2>Update Inspirational Message</h2> 
    <p><form:textarea path="message" rows="8" cols="50"/></p> 
    <span><form:errors path="message" cssStyle="color:      #ff0000; "/></span> 
    <br/> <br/> <br/> 
 
    <h2>Update Best Quote (Required)</h2> 
    <p><form:textarea path="quote" rows="8" cols="50"/></p> 
    <span><form:errors path="quote"  cssStyle="color:      #ff0000; "/></span>  
    <br/> <br/> <br/> 
 
    <p style="padding-top: 15px; padding-left: 20px"><input      class="submit"  
    type="submit" value="Update Home Page"></p> 
  </div> 
  </form:form> 

There are instances when a form view page contains several request parameters which need to be subdivided into groups.

Given this situation, the best strategy is to decompose the huge flat domain POJO into several specific form domain models, each containing related data. Then, create a main POJO component class, which will hold all smaller domain models. This class will be represented as the form-backing object for the form view. A sample implementation of the preceding content page is shown as follows:

package org.packt.personal.web.portal.model.form; 
 
public class Personal { 
 
  private Biography biography; 
  private Education education; 
 
  public Biography getBiography() { 
    return biography; 
  } 
  public void setBiography(Biography biography) { 
    this.biography = biography; 
  } 
  public Education getEducation() { 
    return education; 
  } 
  public void setEducation(Education education) { 
    this.education = education; 
  } 
} 
 
package org.packt.personal.web.portal.model.form; 
import java.util.Date; 
import java.util.List; 
 
public class Biography { 
  private String firstName; 
  private String lastName; 
  private Integer age; 
  private Date birthDate; 
  private String location; 
  private String country; 
  private List<String> hobbies; 
  private List<String> readings; 
  // The getters and the setters 
} 
 
package org.packt.personal.web.portal.model.form; 
public class Education { 
 
  private String educLevel; 
  private String institution; 
  private String degree; 
  private String specialization; 
  private Integer year; 
 
  // The getters and the setters 
} 

The binding of the component will be quite different compared to the usual domain model. To bind all biography attributes to the form components, it is important to access first the getter method of the biography object, then through this we can now access all the setters of its attributes. The following example is a clear picture of the complicated binding:

<form:form commandName="personForm" method="post"> 
  <div class="form_settings"> 
  <!-- insert the page content here --> 
 
    <h2>Update Personal Information</h2> 
    <table style="width:100%; border-spacing:0;"> 
    <tr><td>First Name (*)</td> 
      <td><form:input path="biography.firstName"/></td> 
      <td><form:errors path="biography.firstName" cssStyle="color:        #ff0000"/></td> 
    </tr> 
    <tr><td>Last Name (*)</td> 
      <td><form:input path="biography.lastName"/></td> 
      <td><form:errors path="biography.lastName" cssStyle="color:        #ff0000"/> 
    </tr> 
    <!-- Rest of the script in the sources --> 
  </table> 
 
  <!-- Rest of the code in sources --> 
</form:form> 

The ViewResolver and view configuration

The interface ViewResolver is responsible for the mapping of the view names to the actual view pages. It also provides the view interface, which addresses the request of a view to the view technology. In this PWP project, all of our pages are written in JSP-JSTL so the view interface used is org.springframework.web.servlet.view.JstlView. The project's view resolver is implemented inside the Spring container in this way:

<bean id="viewResolver" 
  class="org.springframework.web.servlet.view.   ResourceBundleViewResolver"> 
  <property name="basename"> 
    <value>config.views</value> 
  </property> 
</bean> 

The views are also declared in the custom property file ./src/config/views.properties:

hello.(class)=org.springframework.web.servlet.view.JstlView 
hello.url=/jsp/hello_world.jsp 
 
index.(class)=org.springframework.web.servlet.view.JstlView 
index.url=/jsp/index.jsp 
 
index_update.(class)=org.springframework.web.servlet.view.JstlView 
index_update.url=/jsp/index_update.jsp 
 
// See the sources 

There are three popular generic ViewResolver implementations that developers always use and these are:

  • InternalResourceViewResolver: This is implemented whenever all the actual view pages are stored inside /WEB-INF/jsp. It has two sets of properties, namely a prefix or suffix that needs to be configured to generate the final view page URL.

            <bean 
              class="org.springframework.web.servlet.view.
                     InternalResourceViewResolver"> 
                <property name="viewClass" 
                value="org.springframework.web.servlet.view.JstlView"/> 
                <property name="prefix"> 
                  <value>/WEB-INF/</value> 
                </property> 
                <property name="suffix"> 
                  <value>.jsp</value> 
                </property> 
            </bean> 
    
  • The prefix indicates the location of the actual views, while the suffix tells the controller the allowed extension of all the actual pages. By default, InternalResourceViewResolver resolves the view names into view objects of type JstlView if the JSTL library is present in the classpath. If the view template is not the default, the viewClass property must be explicitly declared and mapped to other view templates like  org.springframework.web.servlet.view.tiles2.TilesView if tiles are to be used.

  • XmlViewResolver: If you want to declare each individual view-mapping to the actual page in an XML format, then this implementation best fits the project. A sample implementation is shown as follows:

        <bean id="helloWorld" 
          class="org.springframework.web.servlet.view.JstlView"> 
          <property name="url" 
            value="/WEB-INF/helloWorld.jsp" /> 
        </bean> 
  • ResourceBundleViewResolver: This implementation is the most flexible among the three, because the only thing needed here is a property containing all the view mappings. Moreover, one has the capability to combine different view technology in just one project, for the purpose of mixing together presentation layers.

A Spring MVC project can have more than resolvers, given that the order property in all definitions is defined to set order levels of 0 having the highest priority. The following code shows the ordering technique:

<bean 
  class="org.springframework.web.servlet.view.   InternalResourceViewResolver"> 
    <property name="prefix"> 
      <value>/WEB-INF/</value> 
    </property> 
    <property name="suffix"> 
      <value>.jsp</value> 
    </property> 
    <property name="order" value="2" /> 
</bean> 
 
<bean class= "org.springframework.web.servlet.view.XmlViewResolver"> 
  <property name="location"> 
    <value>/WEB-INF/views.xml</value> 
  </property> 
  <property name="order" value="1" /> 
</bean> 
 
<bean class= "org.springframework.web.servlet.view.ResourceBundleViewResolver"> 
  <property name="basename" value="views" /> 
  <property name="order" value="0" /> 
</bean> 

By convention, the InternalResourceViewResolver is always given the least priority because it takes a little time to map the view name directly to the actual pages before all the remaining resolvers. This might give conflict to other mappings if other resolvers are not fast enough in mapping views.

Actual view pages

The view template used in this project is JSP-JSTL, since the view interface used to map to JSP pages is org.springframework.web.servlet.view.JstlView. It is no longer recommended to use scriptlets.

Obviously, the actual view pages use some EL language components like the implicit object sessionScope. EL language is part of the jsp-api.jar libraries, so it is still acceptable to use its components. Moreover, we also used some JSTL tags like <c:out/> and <c:url/>. The tag <c:out/> is always recommended to output values of EL expression ${}, especially in generating reports wherein lots of the data are handled by EL expressions. The attribute escapeXml means that all words that are HTML tags will be captured, and thus they will be rendered on the page as HTML components. But most importantly, redirection implemented inside the view page must use URL rewriting so that when cookies are cut-off, the session data will still be shared by all controllers and views.

Validating Form Data

All form domain objects must be validated using the org.springframework.validation.Validator interface and annotations supported by JSR 303. The validation interface is implemented to create a set of validation rules as per the form view. A sample implementation used in PWP's home content page is shown as follows:

package org.packt.personal.web.portal.validator; 
 
import org.packt.personal.web.portal.model.form.Home; 
import org.springframework.stereotype.Component; 
import org.springframework.validation.Errors; 
import org.springframework.validation.ValidationUtils; 
import org.springframework.validation.Validator; 
 
public class IndexValidator implements Validator { 
  @Override 
  public boolean supports(Class<?> clazz) { 
    return Home.class.equals(clazz); 
  } 
 
  @Override 
  public void validate(Object obj, Errors errors) { 
    Home homeForm = (Home) obj; 
    ValidationUtils.rejectIfEmptyOrWhitespace(errors, 
      "message", "message.empty"); 
    ValidationUtils.rejectIfEmptyOrWhitespace(errors,  
      "quote", "quote.empty"); 
 
    if(homeForm.getMessage().length() > 500) { 
      errors.rejectValue("message", 
        "message.maxlength"); 
    } 
    if(homeForm.getMessage().length() < 50) { 
      errors.rejectValue("message", 
        "message.minlength"); 
    } 
 
    if(homeForm.getQuote().length() > 500) { 
      errors.rejectValue("quote", "quote.maxlength"); 
    } 
    if(homeForm.getQuote().length() < 50) { 
      errors.rejectValue("quote", "quote.minlength"); 
    } 
  } 
 
} 

It validates checks if the user entered a content message and quotes greater than 50, but not greater than 500 alphanumeric. The validator interface has two abstract methods to implement, namely:

  • public boolean supports(): This method checks what type of @ModelAttribute object is being validated. @SessionAttributes are also included in this Boolean method.

  • public void validate(): If the preceding method confirms correctly the @ModelAttribute to be validated, this method will be executed next, dealing with all data values of the domain object.

Validators are components of the Spring MVC project. Spring MVC 4 uses the @Autowired stereotype to call the instance of the validator in the controller class. To enable the validator, the setValidator() method of org.springframework.web.bind .WebDataBinder has to be invoked inside the initBinder() method. To avoid complications, it is recommended to set one validator per initBinder() since we can create more than one initBinder() inside a form controller.

For situations like in PWP where both the @SessionAttributes and @ModelAttribute are utilized by the operations, initBinder() is recommended to be explicitly mapped to the specific attribute for the validator. Otherwise, the following exception shown will be encountered:

SEVERE: Servlet.service() for servlet [pwp] in context with path [/ch01] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Invalid target for Validator [org.packt.personal.web.portal.validator.IndexValidat[email protected]]: undefault] with root cause
java.lang.IllegalStateException: Invalid target for Validator [[email protected]]: undefault

To retrieve the result of the validation on a @ModelAttribute as per initBinder(), be sure to use the @Validated stereotype with the model argument declared in the submitForm() method. Following is a code that shows how to declare and enable validation in a form controller:

@Autowired 
  private Validator indexValidator; 
 
  @InitBinder("homeForm") 
  public void initBinder(WebDataBinder binder) { 
    binder.setValidator(indexValidator); 
  } 
 
  @RequestMapping(method=RequestMethod.POST) 
  public String submitForm(Model model,    @ModelAttribute("homeForm") @Validated Home homeForm,    BindingResult binding) { 
    model.addAttribute("homeForm", homeForm); 
    String returnVal = "index"; 
    if(binding.hasErrors()) { 
      returnVal = "index_update"; 
    } else { 
      model.addAttribute("homeSess", homeForm); 
      model.addAttribute("statusSess", "undefault"); 
    } 
    return returnVal; 
  } 

Validation using JSR 303

Aside from custom validation using the validator interface, Spring 4.x container supports annotations under the JSR 303 specifications that are applied directly to Java beans, used by the classes to impose specific validation rules. The following EmailController uses @NotNull to check if the two objects are not null, otherwise an error will be detected by the BindingResult.

@Controller 
@RequestMapping("/pwp/contact") 
public class EmailController { 
 
  @NotNull 
  @Autowired 
  private SimpleMailMessage emailTemplate; 
 
  @NotNull 
  @Autowired 
  private JavaMailSender emailSender; 
  // See the sources 
} 

Aside from @NotNull, annotations like @Pattern, and @Size are also widely used in string matching and collection size restrictions, respectively.

Domain data type conversion and filtering

The main purpose of the initBinder() is not purely to validate, but to bind request parameter data to the form domain model. It checks if the request parameter data matches the type of variables in the form-backing object. It provides conversion processes to data in order to avoid type mismatch and other related exceptions. The method has built-in property editors that you can use to check types. Some are custom editors of the type  java.beans.PropertyEditorSupport that check complicated matches with added custom transactions.

In the Personal portal page, there are data that needs to be converted into proper object types like age and birth date.

Remember that request parameter data are all, by default, String objects. The custom property editor can be helpful in converting String data to other types, just to fit in the form-backing object's setter methods. Following is a custom editor for this page:

package org.packt.personal.web.portal.converter; 
import java.beans.PropertyEditorSupport; 
 
public class AgeConverter extends PropertyEditorSupport { 
 
  @Override 
  public void setAsText(String text) throws 
  IllegalArgumentException { 
    Integer age = 0; 
    try { 
      age = Integer.parseInt(text.trim()); 
    } catch(Exception e) { 
      age = 18; 
    } 
    setValue(age); 
  } 
} 
 
package org.packt.personal.web.portal.converter; 
 
import java.beans.PropertyEditorSupport; 
import java.text.ParseException; 
import java.text.SimpleDateFormat; 
import java.util.Date; 
 
public class BirthDateConverter extends PropertyEditorSupport { 
 
  @Override 
  public void setAsText(String text) throws 
  IllegalArgumentException { 
    SimpleDateFormat format = new 
    SimpleDateFormat("mm/yy/dd"); 
    Date birthDate; 
    try { 
      birthDate = format.parse(text.trim()); 
    } catch (ParseException e) { 
      // TODO Auto-generated catch block 
      birthDate = new Date(); 
    } 
    setValue(birthDate); 
  } 
} 

E-mail configuration

The Reach Out page opens an electronic communication between the portal owner and the portal reader or user. The Spring Framework supports e-mail operations with the org.springframework.mail package as the root level package with the following API classes:

  • MailSender: The central interface for sending e-mails is the MailSender interface.

  • SimpleMailMessage: The simple value object encapsulating the properties of a simple mail such as from and to (plus many others) is the class.

  • MailException: The root exception of all e-mail checked exceptions which provide a higher level of abstraction over the lower level mail system exceptions.

  • JavaMailSender: The interface that adds specialized JavaMail features such as MIME message support to its superclass MailSender interface.

  • MimeMessageHelper: A class that comes in pretty handy when dealing with JavaMail messages without using verbose JavaMail APIs.

  • MimeMessagePreparator: A callback interface for the preparation of JavaMail MIME messages.

This project sends three forms of e-mail template, namely the basic text-based e-mail, HTML-based e-mail and template-based e-mail. Following is PWP's way of sending an HTML-based e-mail.

public void sendMailHTML(Email emailForm) { 
  String  fromEmail = emailForm.getSendTo(); 
  String toEmail = emailForm.getSendTo(); 
  String emailSubject = emailForm.getSubject(); 
  String emailBody = emailForm.getMessage(); 
 
  MimeMessage mimeMessage = 
  emailSender.createMimeMessage(); 
  try { 
    MimeMessageHelper helper = new 
    MimeMessageHelper(mimeMessage, true, "utf-8"); 
    mimeMessage.setContent("<i><b>"+emailBody  
    +"</b></i>", "text/html"); 
    helper.setFrom(fromEmail); 
    helper.setTo(toEmail); 
    helper.setSubject(emailSubject); 
 
  } catch (MessagingException e) {} 
  /* 
  uncomment the following lines for attachment 
  FileSystemResource 
  file = new FileSystemResource("sample.jpg"); 
  helper.addAttachment(file.getFilename(), file); 
  */ 
 
  emailSender.send(mimeMessage); 
  System.out.println("Mail sent successfully."); 
 
} 
 

The Personal Web Portal (PWP) project


This chapter highlighted how to create a simple Spring MVC project using only its core components. Moreover, the chapter highlighted some of the components used by the PWP that can be used also by anybody to start learning Spring MVC from its core. Learning core components is essential to establish better understanding on how the base framework works, starting from configuring the controller, up to the implementation of PropertyEditorSupport for data binding enhancement and object type conversion. This chapter has given a picture of how to start a base Spring MVC project.

Following are full web pages of the PWP:

  • Let us look at the home page:

  • Now let's look at the personal page:

  • Now, the professional page:

  • And finally the reach out page:

    Note

    Web design theme

    The theme used by this project is inspired by, and based on, templates from http://creativecommons.org.

 

Challenge yourself


Create a personal portfolio website using Spring MVC. The site should consist of the following pages:

  • Home page: Consists of your picture, biography, academic information and other personal-related information.

  • Update page: Updates the information presented on the homepage. See to it that the home page is always updated once this is executed.

  • Contact Us: Provides e-mail support for those who want to email the owner.

 

Summary


Using the basic Spring Framework 4.x APIs, web portal creators can create their own platform to promote their personal philosophy, business, ideology, religion, and other concepts. Although it is an advantage to use existing portal platforms made in other language like PHP and Python, it is still fulfilling to design and develop our own portal based on an open-source framework. The PWP is just prototype software that needs to be upgraded to have a backend database, security, and other social media plugins, in order to make the software commercially competitive.

The next chapter will be about creating a simple document repository focusing on document management. If your personal web portal needs to have a Dropbox-like feature for any types of documents, then the next chapter will help you build that feature.

About the Author

  • Sherwin John Calleja Tragura

    Sherwin John Calleja Tragura started his career as a Java developer by focusing on the Documentum and Alfresco document management systems. His first exposure to mobile applications started in 2010 when he first installed and used the Alfresco mobile application, which featured core document content services. He is currently a technical corporate trainer and a subject matter expert on Java, JEE, and Spring application development. He is a certified EMC Documentum and OCP Java 7 Associate. He authored the following Packt books—Spring MVC Blueprints and Spring 5 Cookbook and also a Packt video course: Jakarta EE 8 Recipes. Before that, he was a technical editor for Alfresco Share, Alfresco 3 Records Management, Mastering Hibernate, and Delphi Cookbook.

    Browse publications by this author

Latest Reviews

(2 reviews total)
Foi rápida e sem problemas
un buon libro non per principianti