Solutions to IT problems

Solutions I found when learning new IT stuff

Spring MVC 3 Tutorial for beginners

with one comment


Introduction

I was trying to create a very simple Web Application using Spring MVC as a usage example for MoleculeDatabaseFramework. Now Spring MVC is obviously overkill for this but it was a good change to learn it. However this endeavor turned out to be much more complicated than I had anticipated. Also it is very hard to find actual tutorials and information on how to use Spring MVC 3. A lot of stuff is from older versions and often it is not mentioned for which version a code or configuration snippet is.

Starting Point

I created a maven web application using netbeans IDE. I added MoleculeDatabaseFramework-1.0.0-SNAPSHOT as dependency. To use the same configuration as my integration test of MoleculeDatabaseFramework use, I had to add further dependencies as they are in the test-scope and not included in MoleculeDatabaseFramework-1.0.0. I also added dependencies spring-web and spring-webmvc, both are required!

Dependency Issues

MoleculeDatabaseFramework project has a dependency on spring-context-3.1.4. In the web application I chose spring-web-3.2.2 and spring-webmvc-3.2.2 which replaced spring-context-3.1.4 with spring-context-3.2.2. The problem is that spring-context-3.2.2 contains less packages than spring-context-3.1.4 and hence I kept getting an exception that EhCacheCacheManager class was missing. After wasting over an hour on this I figured out that since spring-3.2.x this is in a separate dependency spring-context-support-3.2.2. That solved this issue and here the according maven dependencies:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
	<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>3.2.2.RELEASE</version>
</dependency>

Note: this is only required if you are using caching with ehcache in your application.

A second problem was that I was missing class javax.servlet.jsp.jstl.core.Config. This was weird because I could clearly see in netbeans that this was provided by javaee-web-api-6.0 which was added to the project automatically at creation from netbeans. However it is in the provided scope and hence not included in the war-file. To resolve this you need to manually add dependency

<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
	<version>1.2</version>
</dependency>

Putting it all togehter here all dependencies for the project:

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>MoleculeDatabaseFramework</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>0.11.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.6.5</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>4.2.1.Final</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>com.jolbox</groupId>
            <artifactId>bonecp-spring</artifactId>
            <version>0.8.0-rc2-SNAPSHOT</version>
        </dependency>
    </dependencies>

Configuration issues

Configuration File Locations

web.xml Spring configuration file(s) go into the myApp/src/main/webapp/WEB-INF directory or in netbeans it is displayed in myApp/Web Pages/WEB-INF (but the actual path is the prior one). If you have a properties file, that is used by the spring context, like for database config, you need to put it in myApp/src/main/resources and it then can be found with the path classpath:Application.properties. The same is true for the ehcache.xml configuration file. if you use hibernates import.sql file to load data on application start, that belongs there too.

Configuration Files

I created 2 spring configuration files, ApplicationContext.xml and mvc-config.xml. The later is used to configure Spring MVC and ApplicationContext.xml is used to configure
MoleculeDatabaseFramework and requires this entry:

<import resource="mvc-config.xml" />

web.xml

You also need to create a web.xml file in WEB-INF. web.xml contains a reference to the DispatcherServlet that accepts all request:

<servlet>
	<servlet-name>myApp</servlet-name>
	<servlet-class>
		org.springframework.web.servlet.DispatcherServlet
	</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/ApplicationContext.xml
		</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

Note that if you do not specify the contextConfigLocation then the application context must be named -servlet.xml and hence in this case myApp-servlet.xml. load-on-startup must be 1 so that the Spring Context loads on application start.

You also need to specify which URLs you want to map:

<servlet-mapping>
	<servlet-name>MDFSimpleWebApp</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>

This means we want to map all request to myApp.

URL Mapping Issue

After the application actually loaded and my Controller was found and accessed correctly i kept getting an error that my views are not found. Even weirder was that the path to the view displayed in the log was correct!

The solution can be found in this stackoverflow question. However it is not the accepted answer but the one from sourcedelica. You need to add an additional mapping to your web.xml.

<servlet-mapping>
	<servlet-name>jsp</servlet-name>
	<url-pattern>/WEB-INF/jsp/*</url-pattern>
</servlet-mapping>

were the url-pattern is the path to the directory of all your web pages. This leads to following complete web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">
    <display-name>MDF Simple Web Application</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>MDFSimpleWebApp</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/ApplicationContext.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MDFSimpleWebApp</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>/WEB-INF/jsp/*</url-pattern>
    </servlet-mapping>
</web-app>

mvc-config.xml

I chose an annotation based approach for controllers. But all in all the mvc configuration file did not pose any troubles:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
          http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <mvc:annotation-driven />
    <mvc:view-controller path="/" view-name="index"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

ApplicationContext.xml

This file is huge and configures MoleculeDatabaseframework inlcuding spring-data, hibernate, connection pooling and so forth. It is not really relevant to Spring-MVC and hence I’m intentionally omitting it to avoid any confusion. However it must contain at least

<import resource="mvc-config.xml" />

so that the mvc configuration is loaded.

Controller

I created 1 very simple controller and I’m just going to post the source code and explain id:

@Controller
@RequestMapping(value = "/compound")
public class SimpleCompoundController {

    @Autowired
    private SimpleCompoundService compoundService;

    @RequestMapping(value = "/{compoundId}", method = RequestMethod.GET)
    public String getSimpleCompound(@PathVariable Long compoundId, Model model) {
        SimpleCompound compound = compoundService.getById(compoundId);
        model.addAttribute("compound", compound);
        return "compound";
    }
}

@Controller marks the class as controller and hence spring can detect it using component scanning.

@RequestMapping(value = "/compound") on the class level tells this controller handles all request to that url. Note that it is relative to the url specified in the DispatcherServlet in web.xml. In this case this would mean myApp/compound.

@Autowired injects a service loaded in ApplicationContext.xml. This service loads data from the database.

@RequestMapping(value = "/{compoundId}", method = RequestMethod.GET) tells that the annotated method ist repsonsible for handling all request to myApp/compound/{compoundId}. So the value is relative to the mapping of the controller class (if specified). @PathVariable will then take {compoundId} from the URL and pass it into the method in which it is used to fetch the compound wit the given id form the database.

The the compound is added to the model and we return the name of the view to render. This return value is handled by InternalResourceViewResolver configured in mvc-config.xml. The resolver appends the file ending specified (.jsp) and adds the path to the views directory. The actual request will then be for myApp//WEB-INF/jsp/compound.jsp and that jsp page will be rendered.

View

The view is a very simple jsp page. It shows how you can then access the Model in your web page using expressions.

<%@page language="java" contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Compound ${compound.getId()}</title>
    </head>
    <body>
        <div><label>Compound Name:</label><label>${compound.getCompoundName()}</label></div>
    </body>
</html>
Advertisements

Written by kienerj

May 24, 2013 at 12:10

Posted in Java, Programming

Tagged with ,

One Response

Subscribe to comments with RSS.

  1. Reblogged this on Learn as faster u can.

    abhilashranjanblog

    September 24, 2013 at 19:22


Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: