...
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
...
<div class="person">
    <spring:url value="/persons/{id}" var="editUrl">
        <spring:param name="id" value="${person.id}"/>
    </spring:url>
    <sf:form modelAttribute="person" action="${editUrl}" method="POST">
        <table>
            <tr>
                <th>
                    <label for="firstName">
                        <span class="man">*</span>
                        <spring:message code="label.Person.firstname"/> :
                    </label>
                </th>
                <td><sf:input path="firstName"/>
                <sf:errors cssClass="error" path="firstName"/></td>
            </tr>
            ...
            <tr>
                <th>
                    <label for="dateOfBirth">
                        <span class="man">*</span>
                        <spring:message code="label.Person.dob"/> :
                    </label>
                </th>
                <td><sf:input path="dateOfBirth"/>
                <sf:errors cssClass="error" path="dateOfBirth"/></td>
            </tr>

            <tr>
                <th>
                    <label for="gender">
                        <spring:message code="label.Person.gender"/> :
                    </label>
                </th>
                <td>
                    <sf:radiobutton path="gender" value="MALE"/>
                     <spring:message code="label.Person.male"/>
                    <sf:radiobutton path="gender" value="FEMALE"/>
                    <spring:message code="label.Person.female"/>
                </td>
            </tr>
            <tr>
                <th>
                    <label for="hospital">
                        <span class="man">*</span>
                        <spring:message code="label.Hospital"/> :
                    </label>
                    </th>
                    <td>
                        <sf:select path="hospital">
                            <c:choose>
                                <c:when test="${person == null}">
                                    <sf:option value="">
                                    <spring:message code="label.choose"/></sf:option>
                                </c:when>
                                <c:otherwise>
                                    <sf:option value="${person.hospital.id}">
                                                 ${person.hospital.name}
                                   </sf:option>
                                </c:otherwise>
                            </c:choose>

                            <sf:options items="${hospitalList}"
                                 itemValue="id" itemLabel="name"/>
                        </sf:select>

                    </td>
                </tr>
                <tr>
                    <td>
                        <button id="saveButton" type="submit">
                            <spring:message code="command.save"/>
                        </button>
                    </td>
                    <td>
                        <a href="${editUrl}">
                            <spring:message code="command.cancel"/>
                        </a>
                    </td>
                </tr>
            </table>
        </sf:form>
</div>

In the previous code there are a few elements that are underlined. They are Spring form tags equivalent to HTML form tags. The only difference is that they are fully integrated with Spring MVC and their content is populated from the modelAttribute and other Spring form–specific objects, which are covered a little bit later.

The behavior of a Spring form can be described using this succession of steps:

  1. An initial GET request causes the form object to be created and presented to the user.
  2. The user inserts data in the form and sends a POST request to submit the form. In this step, the data inserted is evaluated, validated, and stored in the form object.
  3. POST-Redirect-GET if the operation is successful (if the objective of the POST request has succeeded, a GET request is created to present a confirmation message to the user).

Without the POST-Redirect-GET behavior, an application will behave incorrectly, because subsequent POST requests could lead to duplicate data and/or data corruption. The forms used for search operations do not need a POST request, because submitting a search query has no side effects and it redirects to the results page.

When working with forms, all the fields in a form should map to the fields of an object called a data transfer object (DTO). In the example, this is done for the Search Person form, which is covered later. When editing a person, it is more suitable to use a Person object as a data transfer object. The Person object is an instance of an @Entity class, and maps to a row in the person table in the database. These types of objects are called domain objects. This requires that the object used for this purpose should have a default constructor, getters, and setters for all the fields used in the form. Although using a domain object as a form object welcomes the undesired possibility that some web logic–specific operations might creep in, it is practical to use when the objects handled are simple20 and entity fields are annotated with specific validation annotations (example: @NotEmpty, NotNull, etc.), because implementing a validator class at the web layer may no longer be necessary.

Form-specific data transfer objects are also required when the information from a form is aggregated from multiple domain objects. This allows the form object to encapsulate only what is needed to be displayed on the screen: web layer logic, validation logic, logic for information transfer between the form object, and domain object.

Figure 3-15 shows the correspondence between the form tags and the fields in the CriteriaDto object.

9781484208090_Fig03-15.jpg

Figure 3-15. Form to search a Person

The following can be said when using Spring forms:

  • The CriteriaDto object is linked to the form via modelAttribute="criteriaDto" and it corresponds to the @ModelAttribute annotated object in the controller class.
  • The Spring form has a method attribute the same as an HTML form, and the value of this attribute is the type of request being sent to the server.
    <sf:form action="${personsUrl}" modelAttribute="criteriaDto" method="get">
        ....
    </sf:form>
  • The Spring form has an action attribute the same as an HTML form, and the value of this attribute is the URL of the request.
    <spring:url value="/persons/go" var="personsUrl"/>
     <sf:form action="${personsUrl}" modelAttribute="criteriaDto" method="get">
     ....
    </sf:form>
  • <sf:input path="fieldName"/> is rendered into an HTML input field that is populated with the value of the field named fieldName in the criteriaDto object. Each of the fields defined in a Spring form specifies a path attribute that corresponds to a getter/setter method of the model attribute (in this case the CriteriaDto object). When the page is loaded, Spring calls the getter of each field to populate the form view. When the form is submitted, the setters are called to save the values submitted by the user using the form to the model attribute fields.
  • <sf:select/> elements are rendered into HTML select elements and can be constructed using domain object lists:
    <sf:select path="hospital" itemValue="id" itemLabel="name"
               items="${hospitalList}"/>

    The hospitalList has to be added to the model by the controller as an attribute:

    model.addAttribute("hospitalList", hospitalRepo.findAll());
  • <sf:select/> elements can have customized behavior by using the <sf:option/> and <sf:options/> tag.

For example, if the same form is used for editing and creating a person instance, when a new person is created, you might want to display a default option in the hospital drop-down list, making it obvious for the user that a value has to be selected. That default option is not part of the hospital list model attribute. When a person is edited, you want to select the hospital where that person was born.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
....
<sf:select path="hospital">
    <c:choose>
        <!-- no person attribute, form is used to create a person -->
        <c:when test="${person == null}">
            <sf:option value="">
                 <!-- default option not in the hospital list model attribute -->
                <spring:message code='label.choose'/>
            </sf:option>
        </c:when>
         <!-- form is used to edit a person, person model attribute is set -->
        <c:otherwise>
           <sf:option value="${person.hospital.id}">
                ${person.hospital.name}
            </sf:option>
        </c:otherwise>
        </c:choose>
        <!-- Dynamic list of options -->
        <sf:options items="${hospitalList}" itemValue="id" itemLabel="name"/>
</sf:select>

The preceding example is rendered as an HTML select element containing all hospitals in the hospitalList model attribute and an extra static Choose hospital option when the form is used to create a person. The names of the hospitals are used as labels for the available options in the select element. The JSP c taglib is used for conditional operations.

  • The Spring JSP tag library integrates nicely with other JSP libraries, like Tiles and JSTL.
  • The <sf:errors/> is a Spring special tag for displaying errors. The error messages can be internationalized; this is covered in the “Data Validation” section.

When it comes to using Spring forms, three key subjects must be well understood to use them like an expert: formatting, data binding, and validation; each of these is given the proper coverage in its own section.

Data Formatting

In the Person edit form, you need to display and eventually edit a java.util.Date instance and a Hospital instance. These are complex types and the Spring tag library does not know how to handle them on its own. The developer must provide implementations for the org.springframework.format.Formatter<T> interface for the specific type of object handled in the form. Formatter classes parse text data, turn them into objects, and transform beans into text data ready for rendering. In the 05-pr-mvc-form-solution module, which is the project specific to this section, two formatters are used in the Edit person form; where they are used is shown in Figure 3-16.

9781484208090_Fig03-16.jpg

Figure 3-16. Formatters used in the form to edit a person

Formatters can be used in four places in the application:

  • On the field using annotations (all JSPs pages displaying this annotation use this formatter)
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date dateOfBirth;
  • In the JSP tags (used when multiple JSP pages need to display data differently)
    <fmt:formatDate value="${person.dateOfBirth}" pattern="yyyy-MM-dd" />
  • Registered in the application using the FormattingConversionServiceFactoryBean bean
    <mvc:annotation-driven conversion-service="typeConversionService" "/>

    <!-- Define a custom ConversionService -->
    <bean id="typeConversionService"
           class="o.s.format.support.FormattingConversionServiceFactoryBean">
        <property name="formatters">
            <set>
                <bean class="com.pr.util.DateFormatter"/>
                <bean class="com.pr.util.HospitalFormatter"/>
            </set>
        </property>
    </bean>

    The DateFormatter implementation used in the personal-records project looks like this:

    public class DateFormatter implements Formatter<Date>{
        public static final SimpleDateFormat formatter =
                               new SimpleDateFormat("yyyy-MM-dd");

        @Override
        public Date parse(String s, Locale locale) throws ParseException {
            return formatter.parse(s);
        }

        @Override
        public String print(Date date, Locale locale) {
            return formatter.format(date);
        }
    }
  • On the controller or service method arguments by using implementation of the org.springframework.validation.Validator interface and the @Validated annotation. In the following example, the URI path variable used to request data about a person is of type String and it has to be checked for whether it represents a valid id.
    @Component
    public class IdValidator implements Validator {

        @Override
        public boolean supports(Class<?> clazz) {
            return String.class.equals(clazz);
        }

        @Override
        public void validate(Object target, Errors errors) {
            String id = (String) target;
            if(!id.matches("/{id:\d*}")) {
                errors.reject("id.malformed");
            }
        }
    }

The controller method to retrieve a person’s data and using that validator looks like this:

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String show(@Validated(IdValidator.class)@PathVariable String id,
  Model model) throws NotFoundException {
    Long theId = Long.parseLong(id);
    Person person = personManager.findById(theId);
    if(person == null) {
        throw new NotFoundException(Person.class, theId);
    }
    model.addAttribute("person", person);
    return "persons/show";
}

In the examples in this book, only Date and Hospital formatters are covered because they are the only ones needed in the associated projects, but any complex field type can have a formatter. For example:

  • formatting numbers:
    @NumberFormat(style=Style.NUMBER, pattern="#,###.###")
    private final BigDecimal amount;
  • formatting currencies:
    @NumberFormat(style=Style.CURRENCY)
    private final BigDecimal amount;

When field values are formatted inside a JSP page, formatting annotations are no longer needed. When formatters are registered using the FormattingConversionServiceFactoryBean bean, the specific types are automatically converted without another annotation or tag.

The <mvc:annotation-driven/> and @EnableWebMVC registers default formatters for Numbers and Dates by offering support for specific annotations: @NumberFormat and @DateTimeFormat. If Joda Time is in the classpath, support for it is also enabled.

Java configuration to register custom formatters can be done in two ways: by defining a @Bean of type ConversionService, or by implementing the addFormatters method defined in WebMvcConfigurer (or by overriding it when implementations of this interface are used).

The second way is easier:

\in the @Configuration & @EnableWebMvc annotated class
@Override
    public void addFormatters(FormatterRegistry formatterRegistry)
    {
        formatterRegistry.addFormatter(getDateFormatter());
        formatterRegistry.addFormatter(getHospitalFormatter());
    }

    @Bean
    public DateFormatter getDateFormatter(){
        return new DateFormatter();
    }

    @Bean
    public HospitalFormatter getHospitalFormatter(){
      return new HospitalFormatter();
}

The first way is more complicated. The following is an example of an annotated ConversionService bean definition:

\ in the @Configuration annotated class
    public ConversionService conversionService() {
        FormattingConversionServiceFactoryBean bean =
                 new FormattingConversionServiceFactoryBean();
        bean.setFormatters(getFormatters());
        bean.afterPropertiesSet();
        ConversionService object = bean.getObject();
        return object;
    }

    private Set<Formatter> getFormatters() {
      Set<Formatter> formatters = new HashSet<>();
       formatters.add(dateFormatter);
      formatters.add(dateFormatter);
      return formatters;
    }

// definition for formatter beans as in the previous example

Now you have the conversionService bean. Let’s look at the equivalent for <mvc:annotation-driven conversion-service="conversionService" />:

@FeatureConfiguration
class MvcFeatures {


        @Feature
        public MvcAnnotationDriven annotationDriven(
                        ConversionService conversionService) {
               return new MvcAnnotationDriven().conversionService(conversionService)
        }
        ...
}

MvcAnnotationDriven provides the same options as the XML elements using a conveniently chained method API. But who needs a complicated way to do this when there is an easier way, right?

HospitalFormatter is a custom formatter specifically created to be used in projects attached to this book. It basically transforms a Hospital instance into its name so that it can be rendered in a view. And it takes a hospital id and retrieves the Hospital instance from the database to be returned to the controller, where it is used further. As the HospitalFormatter is a bean like any other, the HospitalManager bean can be injected into it to make this happen. So the custom implementation looks like this:

public class HospitalFormatter implements Formatter<Hospital> {

    @Autowired
    HospitalManager hospitalManager;

    @Override
    public Hospital parse(String text, Locale locale)
                           throws ParseException {
        Long id = Long.parseLong(text);
        return hospitalManager.findOne(id);
    }

    @Override
    public String print(Hospital hospital, Locale locale) {
        return hospital.getName();
    }
}

Data Binding

Form objects, data transfer objects, and domain objects have been mentioned so far. But how are they linked together? How does the information from a form object get transferred to a data transfer object or to a domain object? How does Spring MVC know how to do this? The answer to these three questions is a process named data binding.

Spring MVC binds the request to the form object. When a form is submitted, string data is transformed into objects that are injected into the form object using getters and setters. A POST request or form submission means setters for the form model attribute are called. A GET or page/form load means getters are called upon the form model attribute to populate the view. Each object is identified using the path attribute value in the corresponding Spring element tag. The form object it tightly bound to the JSP page, and if the form object cannot be created, the JSP page won’t be rendered correctly. The form object is linked to the JSP page using the modelAttribute attribute in the <sf:form/> tag:

<sf:form modelAttribute="person" action="${editUrl}" method="POST">
...
</sf:form>

In the controller, the form object can be accessed in multiple ways. It can be received as an argument to the method mapped to the ${editUrl}.

@Controller
public class PersonsController {

    @RequestMapping(method=RequestMethod.POST)
    public String update(Person person) {
    ...
}

In this case, data is copied automatically into the object, and the object is re-created on every request. You can annotate the form object with the @ModelAttribute annotation.

@Controller
public class PersonsController {

   @RequestMapping(method=RequestMethod.POST)
   public String edit(@ModelAttribute("person") Person person) {
   ...
}

Image CC  When the name of the modelAttribute is the same as the name of the argument in a handler method, the value for @ModelAttribute can be skipped. So in the previous case, public String update(@ModelAttribute("person") Person person) is equivalent to public String update(@ModelAttribute Person person).

This annotation was mentioned in the “Redirecting” section; it can be used the same way for forms too, because in this case, you have a controller that handles the edit and show requests for a person instance. @ModelAttribute annotated methods are executed before the chosen @RequestMapping annotated handler method. They effectively pre-populate the implicit model with specific attributes, in this case, the person instance to be displayed or edited.

So you can simplify the controller like this:

@Controller
@RequestMapping("/persons/{id}")
public class PersonsController {

    @ModelAttribute
    public Person findPerson(@PathVariable Long id) {
        return personManager.findOne(id);
    }

    @RequestMapping(method = RequestMethod.GET)
    public String show() {
        return "persons/show";
    }

    @RequestMapping(value="/edit", method = RequestMethod.GET)
    public String edit(Model model) {
        //we add the hospitalList to show in the Hospital drop-down list
        model.addAttribute(hospitalRepo.findAll());
        return "persons/edit";
    }
@RequestMapping(method = RequestMethod.POST)
public String save(Person person, BindingResult result, Model model) {
    if (result.hasErrors()) {
        // we need to add this here as the dropdown list
        // has to be populated correctly
        // and "hospitalList" is not a model attribute
        model.addAttribute(hospitalRepo.findAll());
        return "persons/edit";
    }
    personManager.save(person);
    return "redirect:/persons/".concat(person.getId());
  }

}

In this implementation, you do not have to concern yourself with the existence of the form object because the methods of this controller are only accessible when the URL is constructed with a valid person id.

By default, all fields in a form are binded to the form object, but Spring MVC offers the possibility to modify the default behavior by customizing a WebDataBinder object. Some fields can be blacklisted or whitelisted for the binding process:

@InitBinder
public void initBinder(WebDataBinder binder) {
     //allowed fields
     binder.setAllowedFields("firstName", "lastName");
     //disallowed fields
     binder.setDisallowedFields("pk", "*Pk");
}

The recommended behavior is to whitelist only the necessary fields, even if there might be a lot of them to minimize the security holes.

The validation errors are binded to the form object too using a BindingResult object.

@RequestMapping(method = RequestMethod.POST)
    public String save(@Valid Person person, BindingResult result, Model model) {
        if (result.hasErrors()) {
          return "persons/edit";
        }
    ...
}

If you look at the beginning of this section, where the Edit person form code is, you see that some elements look like this:

<sf:errors cssClass="error" path="dateOfBirth"/></td>

They are right next to their analogue elements:

<sf:input path="dateOfBirth"/>

And they have the exact path attribute value. These elements are used to display validation errors when the POST handler method returns back to the edit view because the BindingResult object was populated by an existing validator bean. When returning to the form, the submitted data is still there, but there is extra information about the state and condition of the submitted data, something more or less like what you see in Figure 3-17.

9781484208090_Fig03-17.jpg

Figure 3-17. Spring default validation errors displayed after a form failed submission

Spring MVC has its own validator messages, but supports externally provided validator messages too. Data binding error messages can be customized and internationalized. The following are some examples; depending on the validation library used, the message keys could be different:

NotEmpty.person.firstName=Please insert First Name Value
Size.person.firstName=Length must be between {2} and {1}
typeMismatch.dateOfBirth=Invalid format, should be 'yyyy-mm-dd'
typeMismatch.amount=Incorrect amount

And after the customization, when a submit fails, the invalidated form looks like what’s shown in Figure 3-18.

9781484208090_Fig03-18.jpg

Figure 3-18. Customized validation errors displayed after a form failed submission

Data Validation

Spring MVC supports JSR 303/349 Bean Validation for validating form objects. If the library javax.validation:validation-api:[version] is in the classpath and the application is configured using <mvc:annotation-driven/> or @EnableWebMvc, it is automatically detected and enabled.

Spring 4+ also supports Hibernate Validator 4.3+, but for the org.hibernate:hibernate-validator:[version] library to be used, a custom validator that implements org.springframework.validation.Validator must be set in the configuration; for example:

<!-- Enables hibernate validator -->
<bean id="validator"
    class="o.s.validation.beanvalidation.LocalValidatorFactoryBean"/>

<!-- Defines basic MVC defaults (handler adapter, mapping,
                date formatting, etc) -->
<mvc:annotation-driven validator="validator"/>

The Hibernate Validator is an extension of the default set of validation annotations provided by the validation-api library, that’s why when using Hibernate Validator, validation-api is enabled by default, as validation-api is a dependency of the Hibernate Validator.

Image !  To depict this, a special Gradle task was created for you in the 00-pr-dao module: allCompileDeps. When executed, Gradle prints the dependency tree for the 00-pr-dao module in the Intellij IDEA console. If you analyze the output, you will find the following snippet.

+--- org.hibernate:hibernate-validator:5.1.3.Final
|    +--- javax.validation:validation-api:1.1.0.Final
|    +--- org.jboss.logging:jboss-logging:3.1.3.GA
|    --- com.fasterxml:classmate:1.0.0

The following are examples of validation annotations:

  • @NotNull: Field cannot be null
  • @Size (min, max): File must have a length in the range (min, max)
  • @Pattern: String not null and matching
  • @NotEmpty: String must not be empty (Hibernate)
  • @Min(val), @Max(val): String must be of length at least minimum, or maximum in size

They are used on the fields of interest in the domain object or data transfer object:

public class Person extends AbstractEntity {

    @Size(min=2, max=50)
    public String firstName;

    @Size(min=2, max=50)
    public String lastName;

    @NotNull
    // comment the following if a custom formatter is registered
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date dateOfBirth;
...
}

The validation is invoked by annotating the form object with @Valid and the errors are registered in the BindingResult object too, alongside the binding errors.

In the JSP form, the way the errors are displayed can also be customized. In the previous section, each error was mapped to its field, but you can also print all the errors in the same place by using the following syntax:

<sf:form modelAttribute="person">
          <form:errors path="*"/>
...
</sf:form>

This approach is not recommended for big forms. It is also quite annoying for the user to have to search for the form field he has to correct. By linking the error to the form field, it becomes quite obvious where the correction must be applied.

The Hibernate Validator contains its own set of internationalization files with default internationalized messages. The Resource bundle is named ValidationMessages; it is located in the hibernate-valdiator.jar under the org.hibernate.validator package. You can expand the hibernate-validator.jar and look at it contents in Intellij IDEA, as shown in Figure 3-19.

9781484208090_Fig03-19.jpg

Figure 3-19. Contents of the hibernate-validator.jar

The message keys in the ValidationMessages.properties files are the message keys set by default in the definition of each annotation. For example, the following is a snippet of code for the @NotEmpty annotation:

@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@NotNull
@Size(min = 1)
public @interface NotEmpty {
    String message() default "{org.hibernate.validator.constraints.NotEmpty.message}";
         ...
}

For every field that fails, the @NotEmpty validation has the default error message printed next to it (if configured so), read from the Hibernate Validator resource bundle files. These messages can be overridden by creating your own ValidationMessages resource bundle in the classpath of the project. Also, the message keys can be customized by making the new message key a parameter for the message property when using the annotation; this allows specific messages to be displayed when the same annotation is used on different fields:

// in the Person entity class
@NotEmpty(message="lastname.notempty")
public String lastName;

#in the ValidationMessages.properties
lastname.notempty=Lastname cannot be empty!

When using Spring forms, the error messages can be part of the application resource bundle under WEB-INFmessages; the message keys usually respect the following template:

constraintName.modelAttributeName.propertyName

Each part of the Spring message key is linked to elements in the application, as depicted in Figure 3-20.

9781484208090_Fig03-20.jpg

Figure 3-20. Spring message keys and linked elements

The message samples at the end of the previous section include customized validation messages, used in the 05-pr-mvc-form-practice and solution modules.

Spring also supports the JEE @Constraint21 annotation, which can be used to define customized validation annotations.

// Pnc.java
@Constraint(validatedBy = [PncValidator.class])
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Pnc {
    //using specific message key
    String message() default "{pncFormatExpected}";
    Class<?>[] groups() default {};
}

//In PncValidator.java
public class PncValidator implements
                        ConstraintValidator<Pnc, String> {
    @Override
    public void initialize(Pnc constraintAnnotation) {
        // nothing to initialize
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return (value == null) || value.matches("[1-2][0-9]*");
    }
}

// In Resouce bundle global.properties files
pncFormatExpected= A valid Personal Numerical Code is required!
// Usage in IdentityCard.java
public class IdentityCard extends AbstractEntity {
...
@Pnc
private String pnc;
...
}

Or the org.springframework.validation.Validator interface can be implemented to provide a custom validator implementation, which can be specific to a controller and can be set using @InitBinder:

class PncValidator extends Validator {
    public void validate(Object target, Errors errors) {
        if ((Person)target)
                        .identityCard.pnc.matches("[1-2][0-9]*") )
            errors.rejectValue("pnc", "pncFormatExpected");
    }

    public boolean supports(Class<?> clazz) {
        return clazz instanceof Person.class;
    }
}

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.setValidator(new PncValidator());
}

Or a validation method can be implemented directly into the data transfer object and called from the controller:

public class CriteriaDto {
    public void validate(Errors errors) {
    if (fieldValue == null || fieldValue.isEmpty())
             errors.rejectValue("fieldValue", "valueExpected");
    }
}
...

@RequestMapping(method=RequestMethod.Get)
public String search(CriteriaDto criteriaDto, BindingResult result) {
    criteriaDto.validate(result);
    // process failure or success normally ...
}

Managing the Form Object

Using a form implies multiple requests, which means that the form object has to be the same across two or more requests (when validations fail, for example). There are more ways to manage the form object:

  • The object is created on every request. This strategy is recommended when creating a new object and the form contains all required data for it.
  • The object is retrieved on every request using a @ModelAttribute annotated method. This strategy works best for editing existing objects. It scales well and it is very simple. The disadvantage is that before every request, the method to retrieve the object is called; thus Spring MVC must bind the request parameters to it. For big forms using data transfer objects representing an aggregation of domain objects, this process can introduce a certain latency because domain objects have to be retrieved from the database and then aggregated.
  • The object is stored in the session between requests. This strategy works for both creating and editing objects. It performs better, but it does not scale because it crowds the session.
  • The object is managed by using flash attributes. This is the best solution for both scenarios, as the object is passed from one request to another by Spring MVC.

In the first case, the object is created in the initial GET request, and then Spring MVC takes care of creating it again and binding all request parameters to it.

@Controller
@RequestMapping("/persons/{id}")
public class PersonsController {

    //1. object is being created on every request
    // initial GET request, object is created
    @RequestMapping(value="/new", method=RequestMethod.GET)
    public String new(Model model) {
        model.add(new Person());
    }

    //2. object is being retrieved
    @ModelAttribute
     public Person findPerson(@PathVariable Long id) {
        return personManager.findOne(id);
    }

    // the POST request - Spring MVC takes care of creating the object and binding
     @RequestMapping(method=RequestMethod.POST)
     public String save(Person person) {
       ...
     }
}

In the second case, the object is retrieved by a manager (service class) before every request and @ModelAttribute annotation on a method is used for this; you can see this in the code sample at the beginning of the “Data Binding” section. The last two cases were covered in the “Redirecting” section.

Summary

After reading this chapter, you should have a wide understanding of Spring Web MVC and all it has to offer. Here is a simple list of topics that you should keep handy when reviewing your new knowledge:

  • What is the DispatcherServlet and what is its role in Spring Web MVC applications?
  • What is the controller programming model?
  • How do you configure Spring MVC applications using XML, Java configuration, mixed cases, and Servlet 3.0 with no web.xml configurations?
  • What are the Spring MVC infrastructure beans and how are they configured?
  • What is the difference between a URL and a URI?
  • How you can create a reusable layout using Tiles for a Spring web application?
  • How do you personalize a Spring MVC application?
  • What types of views does Spring MVC support? What must be provided to do so?
  • How do you chain ViewResolvers to support multiple view types in a single application?
  • How do you configure a ContentNegotiatingViewResolver to support multiple view types for the same resource?
  • How do you create a Spring form?
  • Spring MVC provides a JSP tag library for form rendering. How do you format and validate data handled by the form?
  • How does data binding work?
  • How do you write unit and integration tests to test controllers logic?

Quick Quiz

Question 1: Considering the following configuration in web.xml, what is the name of the parameter that holds the location of the Spring MVC configuration file?

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>o.s.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>?????</param-name>
        <param-value>
            /WEB-INF/spring/mvc-config.xml
        </param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
     <servlet-name>mvc-dispatcher</servlet-name>
     <url-pattern>/</url-pattern>
</servlet-mapping>
  1. contextListener
  2. configurationLocation
  3. contextConfigLocation

Question 2: Considering the following configuration in web.xml, what is the name of the parameter that points to the Spring infrastructure bean that enables Java configuration?

<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>o.s.web.servlet.DispatcherServlet</servlet-class>
<init-param>
    <param-name>???</param-name>
    <param-value>
      o.s.web.context.support.AnnotationConfigWebApplicationContext
    </param-value>
</init-param>
<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        com.book.config.WebConfig
    </param-value>
 </init-param>
 <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  1. contextClass
  2. configClassLocation
  3. contextConfigLocation
  4. contextClassName

Question 3: In web.xml, the servlet name has been configured to mvc-dispatcher. What is the default name of the MVC configuration file that Spring looks for?

  1. mvc-config.xml
  2. mvc-dispatcher.xml
  3. mvc-dispatcher-servlet.xml

Question 4: As a developer, what do you need to do to configure Spring Web MVC application without using an web.xml file ?

  1. Extend the AbstractDispatcherServletInitializer class and override at least createServletApplicationContext and getServletMappings.
  2. Extend the AbstractAnnotationConfigDispatcherServletInitializer class and override at least getServletConfigClasses and getServletMappings.
  3. Implement WebApplicationInitializer.
  4. Extend WebApplicationInitializer and annotate the class with @EnableWebMvc.

Question 5: Which of the following are Spring MVC infrastructure components?

  1. Validator implementations
  2. HandlerAdapter implementations
  3. HandlerMapping implementations
  4. ControllerAdvice implementations

Question 6: The purpose of HandlerMapping implementations is to map incoming requests to the appropriate handlers and a list of pre- and post-processor interceptors. Is this statement true?

  1. Yes
  2. No

Question 7: RequestMappingHandlerMapping is registered by default when the following configuration style is used for a Spring web application:

  1. XML configuration using the MVC namespace specific element <mvc:annotation-driven/>
  2. Java configuration using a configuration class annotated with @EnableWebMVC

Question 8: What are the key interfaces used by Spring to render responses without tying itself to a specific view technology?

  1. View
  2. ViewResolver
  3. ViewConfigurer

Question 9: Which of the following is an out-of-the-box view technology supported by Spring?

  1. JSP
  2. Thymeleaf
  3. Velocity templates
  4. XSLT
  5. Tiles

Question 10: What is the default ViewResolver implementation configured by Spring?

  1. InternalResourceViewResolver
  2. JspResourceViewResolver
  3. UrlBasedViewResolver
  4. BeanNameViewResolver

Question 11: What is the difference between chaining ViewResolver beans and content-type negotiation?

  1. There is no difference.
  2. View Resolver chaining allows supporting multiple view types in a single application.
  3. Content-type negotiation allows support for multiple view types for the same resource.

Question 12: What is true about the HTTP Accept header?

  1. It can be used in a Spring Web MVC application to decide the view type for a resource only when the client is a browser.
  2. It is used for REST web services.
  3. It is useless when the client is a browser.
  4. It can be taken into consideration by setting a value for the ignoreAcceptHeader property in the ContentNegotiatingViewResolver bean.

Question 13: From the following list, select the Spring infrastructure bean types responsible with application personalization:

  1. MessageSource implementations
  2. LocaleChangeInterceptor
  3. LocaleResolver implementations
  4. ThemeResolver implementations

Question 14: What is true about the @ExceptionHandler and @ControllerAdvice annotations?

  1. They are used for handling exceptions thrown by controller methods.
  2. When a method inside a controller is annotated with @ExceptionHandler, this method handles the exceptions thrown only in that controller.
  3. @ControllerAdvice is used at class level; in addition to @ExceptionHandler annotated methods, this class can define other types of methods.

Question 15: Given the following controller, to what request will the call method be mapped to?

@Controller
 @RequestMapping("/persons")
public class PersonsController {

    @RequestMapping("/list")
    public String call(Model model,HttpServletRequest rq) {
    ...
    }
}
  1. http://localhost:8080/persons
  2. http://localhost:8080/persons/list
  3. http://localhost:8080/persons/call

Question 16: Given the following controller, is the declaration of the show method correct?

@Controller
 @RequestMapping("/persons")
public class PersonsController {

    @RequestMapping("/{id}")
    public String show(@PathVariable String number, Model model) {
    ...
    }
}
  1. Yes
  2. No

Question 17: What of the following is something that a Spring MVC handler method could not return?

  1. a string
  2. a Model
  3. a ModelAndView
  4. a JstlView instance
  5. a null value

Question 18: Which of the following statements regarding annotation-based configuration are true?

  1. Annotating a class with Controller is not enough for that class to handle requests; the class also has to extend Spring’s AbstractController class.
  2. @RequestMapping is both used at class and method level.
  3. To enable auto-detection of controller classes, you have to enable component scanning in your configuration.
  4. @ModelAttribute can only be used to annotate controller method arguments.

Question 19: What is true about @ModelAttribute ?

  1. This annotation is used to bind a method parameter or method return value to a named model attribute, exposed to a web view.
  2. If a method is annotated with it, that method will be executed before handling any request.
  3. This annotation is used to bind a form object to a controller.

Question 20: What is @InitBinder used for?

  1. To initialize a controller.
  2. To mark a method that initializes the WebDataBinder, which is used to populate command and form object arguments of annotated handler methods.
  3. To mark a method for execution before handling any request.

Question 21: Which is true when a new view technology is added to a Spring web application?

  1. The view technology in question must provide a class implementing Spring’s View interface.
  2. The view technology in question must provide a class implementing Spring’s ViewResolver interface.
  3. The view technology must require specific configuration beans to be defined.

Question 22: When working with Spring forms, which is the recommended workflow?

  1. A GET request is made to display the form, a POST request is made to submit the data, and a GET request is made to display a confirmation page and prevent multiple resubmissions.
  2. A GET request is made to display the form, and a POST request is made to submit the data.

Question 23: Given the following Spring form definition, what is wrong with it?

<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
...
  <sf:form action="${personsUrl}" method="GET">
  ...
</sf:form>
  1. The method of a form cannot be GET.
  2. The modelAttribute is missing.
  3. The <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> is missing

Question 24: Does Spring MVC support validation of form data?

  1. Yes
  2. No

Question 25: Which of the following are validation annotations used on form object fields?

  1. @NotNull
  2. @Size
  3. @Valid
  4. @NotEmpty
  5. @Constraint
  6. @Required
  7. @Pattern

Practical Exercise

This chapter is quite big, so it has four module projects associated with it and each of the modules covers a specific section. Figure 3-21 depicts the eight module projects attached to this chapter: four practice projects and four proposed solutions for them. You are welcome to analyze the proposed solutions and compare them to your solutions to test your understanding of Spring MVC.

9781484208090_Fig03-21.jpg

Figure 3-21. Practice projects for Chapter 3

All web modules depend on the 01-pr-service module. This project contains the @Service classes and the repositories used to manage data. The 01-pr-service depends on the 00-pr-dao module that contains entity classes and common classes (formatters, enums, and utility classes) used by all other modules. The service module was created to respect the standardized three-tiered software architecture pattern, depicted in Figure 3-22. Each tier has a specific responsibility:

  • The data tier is the data access layer that usually encapsulates persistence mechanisms and exposes the data. It should provide an application programming interface (API) to the logic tier that exposes methods of managing the stored data without exposing or creating dependencies on the data storage mechanisms.22
  • The logic tier (also known as the service layer) controls an application’s functionality by performing detailed processing. This tier is needed when the application needs to be accessed by different type of clients (browsers, web services, etc.).
  • The presentation layer is the topmost level of the application that users can directly access, such as a web page or a desktop GUI.

9781484208090_Fig03-22.jpg

Figure 3-22. Typical standardized three-tiered architecture

Because the Personal Records Manager is quite a small project, the service classes do not do much besides calling analogous methods form repository beans.

The HospitalFormatter is part of the 01-pr-service module because it needs a manager instance to retrieve the hospital instance from the repository.

The DBInitializer class is also located in the service class; it is used to populate the database with some sample entries when a web application starts. This class is a simple bean with access to all the service components used in the application, and with a @PostConstruct annotated method that uses those service classes to insert data. This is the most practical way to initialize a small test-scoped database that does not require external configuration files or additional libraries in the classpath. The bean is annotated with @Component and it is automatically discovered, created, and initialized at application boot time.

The @Service annotated classes are organized in the hierarchy depicted in Figure 3-23.

9781484208090_Fig03-23.jpg

Figure 3-23. Service classes hierarchy

They are all named [EntityType]ManagerImpl, where EntityType is the type of object managed by the class. The Impl suffix is used to emphasize that the class is a concrete implementation for the [EntityType]Manager interface.

The BaseManager interface contains all the basic method skeletons common to all service classes.

  @Transactional
public interface BaseManager<E extends AbstractEntity> {

    @Transactional(readOnly = true)
    List<E> findAll();

    @Transactional(readOnly = true)
    E findById(Long id);

    E save(E e);

    void delete(E e);

    void deleteById(Long id);
}

The manager interfaces extending it add method skeletons specific to each managed entity-type.

The repository components are created using Spring Data JPA and are in fact interfaces extending the JpaRepository interface. This interface extends CrudRepository, which provides sophisticated CRUD functionality for the entity class being managed. All other method definitions that are needed, but not provided, are defined in the interface extending JpaRepository. They are annotated with the @Query annotation, and the query to be executed is set through it; for example:

@Query("select p from Person p where p.lastName = :lastName")
List<Person> getByLastname(@Param("lastName") String lastName);

For Spring to provide proxy repositories with the configured implementation for the interfaces that extend JpaRepository, the following line has been added to the app-dao-config.xml configuration file:

<beans xmlns="http://www.springframework.org/schema/beans"
    ...
      xmlns:jpa="http://www.springframework.org/schema/data/jpa"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
    ...
      http://www.springframework.org/schema/data/jpa
      http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
    ...">
  <jpa:repositories base-package="com.pr.repos"/>
</beans>

The equivalent Java configuration makes use of the @EnableJpaRepositories annotation:

@Configuration
@EnableJpaRepositories("com.pr.repos")
class ApplicationConfiguration {

  @Bean
  public EntityManagerFactory entityManagerFactory() { ... }
  @Bean
  public DataSource dataSource() {...}

...
{

The basic configuration *.gradle file for each web module project looks like this:

(1) apply plugin: 'war'
apply from: 'https://raw.github.com/akhikhl/gretty/master/pluginScripts/gretty.plugin'

dependencies {
      (2) compile project(':01-pr-service')
      compile misc.slf4jApi, misc.slf4jJcl, misc.logback,
              hibernate.ehcache, hibernate.em, hibernate.core, hibernate.validator,
              spring.jdbc, spring.orm, spring.contextSupport, spring.data,
              spring.webmvc,
              misc.dbcp, misc.h2, misc.joda, misc.jstl, misc.tilesJsp,
              misc.tilesReqApi, misc.javaxEl
      testCompile tests.junit, tests.mockito, spring.test,
           tests.hamcrestCore, tests.hamcrestLib
}

gretty {
    port = 8080
  (3) contextPath = '/mvc-layout'
}
  1. This line is where the Gretty plugin is defined to run this module.
  2. This is the line where the dependency from the 01-pr-service project is defined. This line ensures that before compiling the web application, the 01-pr-service is compiled first.
  3. This is the line where the Gretty plugin is configured to start the application with a different context than the name of the project. In the case presented here after starting the application, the web interface can be accessed at http://localhost:8080/mvc-layout This was necessary for practical reasons, because by default the module name is used, and the names of the modules in this chapter are quite long.

All the projects can be built without the tests by running the allCompile task, as mentioned earlier in the book. In case you forgot, the task can be found directly under the project root, in this case the personal-records project, in the Intellij IDEA Gradle task tab, as you can see in Figure 3-24.

9781484208090_Fig03-24.jpg

Figure 3-24. The Gradle allCompileTask

The other two tasks you need to use are the appStart and appStop under the module-project name in the Intellij IDEA Gradle task tab; they start and stop the web application, as depicted in Figure 3-25.

9781484208090_Fig03-25.jpg

Figure 3-25. The Gerry start and stop web application tasks

All the modules in personal-records can be built and run separately when using Gradle outside of Intellij IDEA, by using the command line in a terminal and running specific Gradle tasks:

$ cd personal-records
$ gradle :02-pr-mvc-basic-practice:build
$ gradle :02-pr-mvc-basic-practice:run

In the previous examples, the :02-pr-mvc-basic-practice is the name of the submodule, and :build and run are Gradle tasks to be run for the modules.

Image !  When running examples in the command line, the run task is used instead of appStart to run the web modules in Gretty, so the execution can be ended by pressing any key in the terminal.

Each of the projects suffixed with -practice is incomplete, missing either a bean definition or a configuration. In its place there is a TODO comment explaining what you have to do to make the project build. Each project covers a specific topic from the chapter, as follows:

  • 02-pr-mvc-basic-practice is a simple Spring web application project that displays a list of persons from the applications. It should contain proper definitions for all the personalization beans you read about in the chapter; it is what the TODOs are all about: configuring the personalization beans properly. It has only one controller, the PersonsController, which is used to populate the list.jsp and show.jsp views. This controller also has an @ExceptionHandler method that handles cases when a link is manually created with a non-existing person id. (Test the exception handling method by manually accessing http://localhost:8080/mvc-basic/persons/99.)
  • 03-pr-mvc-layout-practice is a simple Spring web application project that uses the Tiles engine to create views. Some configuration is missing and some methods have to be added for the project to work correctly. The Tiles template for the application is found under webapp/WEB-INF/templates; it is called layout.jsp. You can see the full path within the project in Figure 3-26.

9781484208090_Fig03-26.jpg

Figure 3-26. Path of the Tiles layout template

There are two controllers defined PersonsController and HospitalsController. Each of the controllers has the responsibility of populating the corresponding list.jsp views and the HospitalsController has a method that uses redirect: and redirectAttributes. This project also contains unit and integration tests designed to test you controllers.

  • 04-pr-mvc-view-practice is a simple Spring web application project that uses content view negotiation to present the data to the user in a specific view format for the persons/list URL. All it is missing is a proper configuration for the ContentNegotiatingViewResolver bean.
  • 05-pr-mvc-form-practice is a simple Spring web application project that presents the user with a form to edit users and one to search for users. The TODO tasks require the user to place the correct annotation on methods to enable validation and finish implementing the person search form. The part of the application managing Hospital entries has been removed for the purpose of simplicity, but if you want to practice your Spring form skills, you are welcome to try to create an edit form for a Hospital entry after you have solved the existing TODOs.

All the applications are internationalized and themed. There are two languages available—English and German, and two colored themes—blue and green.

When all the tests pass, all the applications start successfully and provide the expected functionality, and you feel confident that you have a solid grasp of the Spring Web MVC, you can continue to the next chapter.

________________________

1Thymeleaf is the new sheriff in Web town. It is an XML/XHTML /HTML5 template engine that works both in web and non-web environments. It is really easy to integrate it with Spring. If you want to read more about it before using it in the practice code for this chapter, go to the official site at http://www.thymeleaf.org/.

2If it looks as if Spring MVC resembles Struts, you are definitely not imagining things. The Spring Web MVC was inspired by Struts, which was one of the first MVC-based frameworks. The DispatcherServlet in Spring has the same responsibilities as the ActionServlet in Struts, as both are implementations of the Front Controller Pattern. You can read more about this software design pattern at http://www.martinfowler.com/eaaCatalog/frontController.html.

3If you want, you can look in the API documentation for detail information about this class, which is available at http://docs.spring.io/spring/docs/current/javadoc- api/.

4The code for this class is at https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/ SpringServletContainerInitializer.java.

5The contents can be accessed directly on GitHub at https://github.com/spring-projects/ spring-framework/blob/master/spring-webmvc/src/main/resources/org/springframework/web/ servlet/DispatcherServlet.properties.

6The full list of configuration options for Gretty can be found at http://akhikhl.github.io/gretty-doc/Gretty-configuration.html.

7http://docs.spring.io/spring/docs/current/spring- framework-reference/htmlsingle/#mvc-ann-requestmapping-31-vs-30, http://docs.spring.io/spring/docs/current/spring- framework-reference/htmlsingle/#mvc-config-enable.

8http://docs.spring.io/spring/docs/4.1.x/spring- framework-reference/htmlsingle/#mvc-viewresolver.

9You can read more about it in the official documentation at http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans- p-namespace.

10By default, strategies for checking the extension of the request path and the Accept header are registered. The path extension check performs lookups through the ServletContext and the JavaBeans Activation Framework (if present) unless media types are configured. In order to use the JavaBeans Activation Framework, the activation.jar has to be in the classpath of the application.

11A detailed explanation of exception handling using Spring MVC is at https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc.

12A RESTful URI identifies a domain resource (like a book, or a person, in this case) rather than an application resource like a web page or a form. URI is the acronym for Uniform Resource Identifier. URL is the acronym for Uniform Resource Locator. REST services work only with URIs and @PathVariable.

13More information about it is at http://mockito.org.

14Read more about it on the official site at http://hamcrest.org/.

15You can take a look at these interceptors’ code on GitHub at https://github. com/spring-projects/spring-framework/blob/master/spring- webmvc/src/main/java/org/ springframework/web/servlet/theme/ThemeChangeInterceptor.java and https://github.com/spring-projects/spring-framework/blob/master/spring-webmvc/src/main/java/org/springframework/web/servlet/i18n/LocaleChangeInterceptor.java.

16This approach is described by the Composite View pattern that introduces the notions of composite and atomic views. A composite view is a tree structure of atomic views. An atomic view can be included dynamically and it changes based on the context.

17Detailed API information on tiles:insertAttribute is at https://tiles.apache.org/framework/tiles-jsp/tlddoc/tiles/insertAttribute.html.

18Detailed API information on tiles:importAttribute is at https://tiles.apache.org/framework/tiles-jsp/tlddoc/tiles/insertAttribute.html.

19A full discussion is at http://forum.thymeleaf.org/why-Thymeleaf-td3412902.html.

20Adam Bien, one of the most respected Java developers in the world, has named DTOs objects “anemic in general and do not contain any business logic” on his blog at http://www.adam-bien.com/roller/ abien/entry/value_object_vs_data_transfer.

21See http://docs.oracle.com/javaee/7/api/javax/validation/Constraint.html.

22In the PErsonal REcords Manager project, because Spring DATA JPA is used, the implementation of the repositories API is reduced to interfaces extending the JpaRepository interface. They are placed in the logic tier/service layer.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.150.123