Java – Tomcat / Spring integration returning 404

eclipsejavaspringspring-mvctomcat

I'm having a similar issue to HTTP Status 404 on Spring 3.1 MVC app

But I have not made the same mistakes as that poster did, yet I'm still getting a 404.

web.xml file:

<?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"
version="2.5">

<display-name>Test</display-name>

<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

<servlet>
<servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

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

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>

I am able to load index.jsp but not an actual controller.

dispatcher-servlet.xml file:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" 
xsi:schemaLocation="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">

<!-- enable Spring’s component scanning -->
<context:component-scan base-package="com.test" />

<!-- process the @RequestMapping annotations at the class level -->
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />

<!-- process the @RequestMapping annotations at the method level -->
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

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

Controller

package com.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.ui.Model;

@Controller
public class BenchController {

@RequestMapping(value = "/bench", method = RequestMethod.GET)
@ResponseBody
public String welcome(Model model) {
  return "yo";
}

}

I am able to go to localhost:8080/Test/index.jsp but localhost:8080/Test/bench returns a 404.

I am using eclipse, if that helps.

EDIT 1:

This is the logging.properties. I can't find any of the logs apart from out log.

handlers = 1catalina.org.apache.juli.FileHandler, 2localhost.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

.handlers = 1catalina.org.apache.juli.FileHandler, java.util.logging.ConsoleHandler

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

1catalina.org.apache.juli.FileHandler.level = FINE
1catalina.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
1catalina.org.apache.juli.FileHandler.prefix = catalina.

2localhost.org.apache.juli.FileHandler.level = FINE
2localhost.org.apache.juli.FileHandler.directory = ${catalina.base}/logs
2localhost.org.apache.juli.FileHandler.prefix = localhost.

java.util.logging.ConsoleHandler.level = FINE
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level = INFO
org.apache.catalina.core.ContainerBase.[Catalina].[localhost].handlers = 2localhost.org.apache.juli.FileHandler

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
#org.apache.catalina.startup.ContextConfig.level = FINE
#org.apache.catalina.startup.HostConfig.level = FINE
#org.apache.catalina.session.ManagerBase.level = FINE
#org.apache.catalina.core.AprLifecycleListener.level=FINE

Interesting enough, I'm not sure where catalina.base variable is being defined.

EDIT 2:

pom.xml

<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>com.test</groupId>
  <artifactId>Test</artifactId>
  <version>0.1</version>
  <packaging>war</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring.version>3.1.0.RELEASE</spring.version>
  </properties>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
      <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>            
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <configuration>
          <webXml>/WebContent/WEB-INF/web.xml</webXml>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.16</version>
    </dependency>
  </dependencies>
</project>

I don't see log4j in my dependency tree even though it's in my pom file.

EDIT 3:

Here's how I created my project in eclipse:

  1. New Maven Project (m2 eclipse plugin)
  2. I selected Simple Project
  3. Filled out group/artificat id and selected war as my packaging
  4. I right clicked on my project in the project folder window and selected Properties
  5. I clicked on Project Facets and converted it when prompted
  6. I selected "Dynamic Web Module" and clicked ok

Once I had all that done , I started adding the dispatcher, updated the pom with spring, etc.

EDIT 4:

Alright, I'm finally getting somewhere. After commenting out this lines in web.xml:

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

I was able to directly deploy the application through tomcat manager. Now I'm getting

java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/dispatcher-servlet.xml]

It seems it's trying to find the dispatcher-servlet.xml under /WEB-INF folder. After looking inside the war file, I cannot locate the file at all. Every time I place the servlet xml in that folder, it seems to be removed. I can put it under any other folder, but for some reason when I do, it still seems to search for the file under /WEB-INF even after updating the web.xml to reference the new location.

EDIT 5:

Here's an update on my 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"
version="2.5">

  <display-name>Test</display-name>

  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

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

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>WEB-INF/classes/applicationContext.xml</param-value>
  </context-param>

  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>WEB-INF/classes/log4j.properties</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>

</web-app>

After explicitly adding the location of applicationContext and dispatcher-servlet. catalina.out says:

Mapped URL path [/bench] onto handler 'benchController'
Mapped URL path [/bench.*] onto handler 'benchController'
Mapped URL path [/bench/] onto handler 'benchController'
Mapped URL path [/bench] onto handler 'benchController'
Mapped URL path [/bench/*] onto handler 'benchController'
FrameworkServlet 'dispatcher': initialization completed in 245 ms 

But when going to localhost:8080/Test/bench – it still returns a 404.
I cannot acess index.jsp when I deploy through tomcat manager, but I can if I deploy through eclipse. No other output by catalina.out.

Log:

127.0.0.1 - - [20/Feb/2012:14:41:13 -0800] "GET /Test/ HTTP/1.1" 200 182
127.0.0.1 - - [20/Feb/2012:14:41:19 -0800] "GET /Test/ HTTP/1.1" 200 182
127.0.0.1 - - [20/Feb/2012:14:41:24 -0800] "GET /Test/bench HTTP/1.1" 404 1012

SOLUTION:

I followed the instructions provided below and inside the project folder, from the command line I ran mvn eclipse:eclipse -Dwtpversion=2.0 so that eclipse can import the project correctly. I'm working on figuring out how to do this all through eclipse to avoid having to manually do this every time.

Best Answer

You mentioned you wanted to learn how to make Spring MVC web app from scratch - here's the list of steps to produce a working app with your configuration:

1) create a folder a

2) in that folder create a file pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>test</groupId>
<artifactId>test</artifactId>
<name>test</name>
<packaging>war</packaging>
<version>1.0.0-SNAPSHOT</version>
<properties>
    <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
</properties>
<dependencies>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${org.springframework.version}</version>
    </dependency>
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.16</version>
        <scope>runtime</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <configuration>
                <warName>test</warName>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>2.3.2</version>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
    </plugins>
</build>
</project>

3) in folder a create sub folders src/main/java/test and put file YoController.java there:

package test;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class YoController {

    @RequestMapping(value = "/bench", method = RequestMethod.GET)
    @ResponseBody
    public String welcome(Model model) {
        return "yo";
    }
}

4) create subfolders src/main/webapp/WEB-INF and put two files there, web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <servlet>
        <servlet-name>dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

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

</web-app>

and dispatcher-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="test" />

</beans>

5) create subfolders src/main/resources and put log4j.properties file there:

log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

6) in command prompt from folder a type: mvn package

7) copy target/test.war to Tomcat's webapps folder

8) start Tomcat and go to URL localhost:8080/test/bench

I've just completed all the steps and it worked. Logging shows Spring messages that will tell you if something is wrong.