---
title:   Spring 3.1 MVC skeleton project upgrade
date:   
lang:    en
origin:  blogger → here
---

Spring 3.1 MVC skeleton project upgrade

Over the weekend I upgraded my Spring project skeleton to include Spring 3.1 MVC support.

The skeleton app uses Scaml, a HAML-variant, as its view layer. The index page merely lists all the examples we have available.

It also contains the standard dependencies I usually recommend: Spring Framework 3.1, Joda-Time 2.0, Guava 10, LogBack/SLF4j, JUnit, Mockito and PostgreSQL.

The skeleton application is available as a Maven project at my BitBucket page at http://dev.gueck.com/project-skeletons/src/tip/spring3-web. It can be executed with the command mvn -Dspring.profiles.active=dev jetty:run.

#!haml
-@ var examples:java.util.List[com.gueck.spring3.Example]
!!! 1.1
%html
  %head
    %title Example application with #{examples.size} examples.
  %body
    %form(method='POST' action='/submitExample')
      %input(type='text' name='name')
      %input(type='submit' value='OK')
    %p Our Examples:
    - for(x <- examples)
      %p.example<
        %a(href='/edit/#{x.id}') #{x.name}!

The embedded development database gets re-initialized with a schema and test data at every application restart.

src/main/resources/com/gueck/spring3/dev-schema.sql:

#!sql
CREATE TABLE examples (
  id IDENTITY,
  name VARCHAR(65536)
);

src/main/resources/com/gueck/spring3/dev-test-data.sql:

#!sql
INSERT INTO examples (name)
VALUES ('The Wire'), ('The West Wing'), ('The Daily Show');

The Spring MVC controller, service and DAO classes don't need much introduction.

src/main/java/com/gueck/spring3/ExampleController.java:

#!java
package com.gueck.spring3;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class ExampleController {
    
    @Autowired
    private ExampleService exampleService;
    
    @Autowired
    private ExampleDao dao;
    
    @RequestMapping("/")
    public String welcome(final ModelMap model) {
        model.put("examples", dao.listExamples());
        return "index";
    }

    @RequestMapping(value="/submitExample", method=RequestMethod.POST)
    public String submitExample(@RequestParam final String name) {
        exampleService.submitNewExample(new Example(name));
        return "redirect:/";
    }
}

The service makes the business decisions, such as whom to exclude, and also demonstrates Guava's usefulness in collection handling.

src/main/java/com/gueck/spring3/ExampleService.java:

#!java
package com.gueck.spring3;

import static com.google.common.collect.Iterables.any;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ExampleService {

    @Autowired
    private ExampleDao dao;
    
    private static Predicate<String> contains(final String match) {
        return new Predicate<String>() {
            @Override public boolean apply(final String input) {
                return match.toLowerCase().contains(input.toLowerCase());
            }
        };
    }
    
    private ImmutableList<String> badWords = ImmutableList.of("republican", "religion", "true finns");
    
    public boolean submitNewExample(final Example example) {
        if (any(badWords, contains(example.name))) {
            return false;
        } else {
            return dao.createExample(example);
        }
    }
}

The easiest way to do a simple CRUD DAO is with Spring JDBC, but it gets progressively more complex as your domain tables/classes get more complex, and pretty soon you're better off with just going with JPA.

src/main/java/com/gueck/spring3/ExampleDao.java:

#!java
package com.gueck.spring3;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class ExampleDao {
    
    private static final String INSERT_EXAMPLE = "INSERT INTO examples (name) VALUES (?)";

    private static final String SELECT_EXAMPLE = "SELECT * FROM examples WHERE id = ?";
    
    private static final String UPDATE_EXAMPLE = "UPDATE examples SET name = ? WHERE id = ?";
    
    private static final String DELETE_EXAMPLE = "DELETE FROM examples WHERE id = ?";
    
    private static final String LIST_EXAMPLES = "SELECT * FROM examples";
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public boolean createExample(final Example example) {
        return 1 == jdbcTemplate.update(INSERT_EXAMPLE, example.name);
    }
    
    public Example readExample(final long id) {
        return jdbcTemplate.queryForObject(SELECT_EXAMPLE, Example.MAPPER, id);
    }
    
    public boolean updateExample(final Example example) {
        return 1 == jdbcTemplate.update(UPDATE_EXAMPLE, example.name, example.id);
    }
    
    public boolean deleteExample(final long id) {
        return 1 == jdbcTemplate.update(DELETE_EXAMPLE, id);
    }
    
    public List<Example> listExamples() {
        return jdbcTemplate.query(LIST_EXAMPLES, Example.MAPPER);
    }
}

The skeleton domain class uses Guava's ToStringHelper, and contains a static instance of Spring JDBC's RowMapper.

I like to situate some or many of the Spring JDBC helpers in the actual domain class, but the location varies depending on the various architectural visions you might embrace.

src/main/java/com/gueck/spring3/Example.java:

#!java
package com.gueck.spring3;

import com.google.common.base.Objects;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;

/**
 * Example domain class.
 */
public class Example {
    
    public Long id;
    
    /** A name to show in a user interface. */
    public String name;

    public Example() {
    }

    public Example(final String name) {
        this.name = name;
    }

    public static final RowMapper<Example> MAPPER = new RowMapper<Example>() {
        @Override
        public Example mapRow(ResultSet rs, int rowNum) throws SQLException {
            final Example ret = new Example();
            ret.id = rs.getLong("id");
            ret.name = rs.getString("name");
            return ret;
        }
    };

    @Override
    public String toString() {
        return Objects.toStringHelper(this)
                .add("id", id)
                .add("name", name)
                .toString();
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(id, name);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Example other = (Example) obj;
        if (!java.util.Objects.equals(this.id, other.id)) {
            return false;
        }
        if (!java.util.Objects.equals(this.name, other.name)) {
            return false;
        }
        return true;
    }
}

The Spring XML configuration file now demonstrates the new mvc:resources-tag, as well as profiles, and how to create a embedded development database with them.

src/main/webapp/WEB-INF/springmvc-servlet.xml:

#!xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:c="http://www.springframework.org/schema/c"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->

    <!-- Scans within the base package of the application for @Components to configure as beans -->
    <!-- @Controller, @Service, @Configuration, etc. -->
    <context:component-scan base-package="com.gueck.spring3" />

    <!-- Enables the Spring MVC @Controller programming model -->
    <mvc:annotation-driven />
    
    <mvc:resources mapping="/resources/**" location="/WEB-INF/resources/" />
        
    <bean id="viewNameTranslator" class="org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator"/>
    
    <bean id="scalateViewResolver" class="org.fusesource.scalate.spring.view.ScalateViewResolver"
        p:order="1" p:prefix="/WEB-INF/view/" p:suffix=".scaml" />

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
         p:order="2" p:viewClass="org.springframework.web.servlet.view.JstlView" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />

    <context:property-placeholder />

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />
    
    <beans profile="production">
        <bean id="dataSource" class="org.postgresql.ds.PGPoolingDataSource" destroy-method="close">
            <property name="serverName" value="${db.serverName}"/>
            <property name="databaseName" value="${db.databaseName}"/>
            <property name="user" value="${db.username}"/>
            <property name="password" value="${db.password}"/>
        </bean>
    </beans>
    
    <beans profile="dev">
        <jdbc:embedded-database id="dataSource" type="HSQL">
            <jdbc:script location="classpath:com/gueck/spring3/dev-schema.sql"/>
            <jdbc:script location="classpath:com/gueck/spring3/dev-test-data.sql"/>
        </jdbc:embedded-database>
    </beans>

</beans>