Spring4 MVC not recognizing jsps - spring-mvc

I am trying to make an MVC project using gradle and Spring4.
#Bean
public UrlBasedViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
...
#RequestMapping("/home")
public String welcome() {
return "index";
}
But when I run using gradle jettyRun I get...
http://localhost:8080/personal-war/home
HTTP ERROR 404
Problem accessing /personal-war/WEB-INF/jsp/index.jsp. Reason:
NOT_FOUND
Update 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" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Spring MVC Application</display-name>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.proj.spring.config</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>
</web-app>
I added this line
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
but then only html renders the server side stuff isn't working

First, you need to know that Servlet containers (I'll assume Jetty too) have a Servlet for rendering JSPs. This Servlet is typically extension mapped to *.jsp.
The Servlet Specification gives the order of url-pattern matching
The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the
servlet.
The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory
at a time, using the ’/’ character as a path separator. The longest
match determines the servlet selected.
If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles
requests for the extension. An extension is defined as the part of
the last segment after the last ’.’ character.
If neither of the previous three rules result in a servlet match, the container will > attempt to serve content appropriate for the resource requested. If a "default" servlet is defined for the application, it will be used. Many containers provide an implicit
default servlet for serving content.
In your case, when you forward to
/WEB-INF/jsp/index.jsp
The Servlet container will match that path to your DispatcherServlet mapped to
/*
This happens before the container could match the JSP servlet mapped to *.jsp.
It therefore uses your DispatcherServlet to service(..) the request. But your DispatcherServlet does not have a handler for
/WEB-INF/jsp/index.jsp
The simple solution would be to map your DispatcherServlet to
/
and have it be the fallback Servlet if no match is found.

Related

getRequestDispatcher(.).forward(req,res) throws java.io.FileNotFoundException

I have upgraded my Servlet from 2.4 to 3.0 and deployed my application on Websphere 8.5.5.8. Application Server starts properly.
When I try to access my home.jsp page in browser it throws:
Controller Main Error OG1000SRVE0190E: File not found: /servlet/com.platform7.affina.operations.servlet.ValidateLoginUser
When I try to debug, code hits my Main Controller Servlet (which is in same package) but inside Controller servlet class I am calling:
this.getServletContext().getRequestDispatcher("Servlet/com.platform7.affina.operations.servlet.ValidateLoginUser").forward(request, response);
Which throws:
FileNotFoundException for Servlet/com.platform7.affina.operations.servlet.ValidateLoginUser.
But ValidateLoginUser is in the same package and classes folder location!
Folder structure:
\NEED4SPEEDCell02\operations_1.ear\OperationsWeb.war\WEB-INF\classes\com\platform7\affina\operations\servlet
ControllerMain.class and ValidateLoginUser.class are in same servlet package.
my Web.xml file:
<servlet>
<servlet-name>servletMain</servlet-name>
<servlet-class>com.platform7.affina.operations.servlet.ControllerMain</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servletMain</servlet-name>
<url-pattern>/controllerMain</url-pattern>
</servlet-mapping>
So when I access my URL: it hits ControllerMain.class but inside this class I am calling another servlet which is not part of web.xml but is located in same package of ControllerMain.class.
When I print realpath: this.getServletContext().getRealPath("/"));
I get:
C:\WebSphere858\AppServer\profiles\AppSrv01\installedApps\NEED4SPEEDCell02\operations_1.ear\OperationsWeb.war
I tried using getNamedDispatcher(..) too but throws: null.
Same code works fine on Websphere 7 and even works on Websphere 8.5.5.5
Due to security reasons the default setting for com.ibm.ws.webcontainer.disallowServeServletsByClassname property has been changed.
Please Note:This APAR has changed the default value of the
WebContainer custom property
com.ibm.ws.webcontainer.disallowServeServletsByClassname from false to
true so that no security threat could occur. Prior to this change, it
was up to the developer to remember to change the custom property to
true before deploying into production.
Property Name:
com.ibm.ws.webcontainer.disallowServeServletsByClassname Description:
If set to true, disallows the use of serveServletsByClassnameEnabled
at the application server level, overriding any setting of
serveServletsByClassnameEnabled at the application level. This
property affects all applications. Values: true(default)/false
You will need to add that custom property to the Web Container and set it to false for serving servlets by class name.
But as BalusC suggested, you should add your servlet to web.xml in the form:
<servlet>
<servlet-name>servletMain</servlet-name>
<servlet-class>com.platform7.affina.operations.servlet.ValidateLoginUser</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servletMain</servlet-name>
<url-pattern>/validateLoginUser</url-pattern>
</servlet-mapping>
and change that forward to:
this.getServletContext().getRequestDispatcher("/validateLoginUser").forward(request, response);
And do the same with your other class from the same package.
You seem to be relying on the legacy InvokerServlet which is known to have major security holes. This was deprecated in Tomcat 5 and clones (WebSphere 4) and removed in Tomcat 7 and clones (WebSphere 6).
You're not supposed to use it anymore. Just map the servlet on a normal URL pattern and invoke it. Assuming that the servlet is mapped on an URL pattern of /validateLoginUser via #WebServlet("/validateLoginUser") annotation on the servlet class, or via <url-pattern>/validateLoginUser</url-pattern> in web.xml mapping on the servlet, then you can get a request dispatcher on it as below:
request.getRequestDispatcher("/validateLoginUser");
Or, just refactor shared code to a plain Java class with a method and invoke it the usual Java way. It's these days kind of weird to have shared validation logic tight coupled in a servlet.
See also:
How to invoke a servlet without mapping in web.xml?
To make above upgrade working, I did few other changes as below for future references.
Mainly, I have to change binding files for websphere.
Previously, I had two bindings ibm-web-bnd.xmi and ibm-web-ext.xmi
ibm-web-bnd.xmi
<?xml version="1.0" encoding="UTF-8"?>
<com.ibm.ejs.models.base.bindings.webappbnd:WebAppBinding xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI" xmlns:com.ibm.ejs.models.base.bindings.webappbnd="webappbnd.xmi" xmi:id="WebAppBinding_1226331113121" virtualHostName="default_host">
<webapp href="WEB-INF/web.xml#WebApp"/>
<resRefBindings xmi:id="ResourceRefBinding_1226331113121" jndiName="AffinaDataSource_pma">
<bindingResourceRef href="WEB-INF/web.xml#ResourceRef_AffinaDataSource_pma"/>
</resRefBindings>
</com.ibm.ejs.models.base.bindings.webappbnd:WebAppBinding>
ibm-web-ext.xmi
<?xml version="1.0" encoding="UTF-8"?>
<com.ibm.ejs.models.base.extensions.webappext:WebAppExtension
xmi:version="2.0" xmlns:xmi="http://www.omg.org/XMI"
xmlns:com.ibm.ejs.models.base.extensions.webappext="webappext.xmi"
xmi:id="WebAppExtension_1226331113121"
serveServletsByClassnameEnabled="true">
<webApp href="WEB-INF/web.xml#WebApp"/>
<jspAttributes xmi:id="JSPAttribute_1226331113121" name="reloadEnabled" value="true"/>
<jspAttributes xmi:id="JSPAttribute_1226331113122" name="reloadInterval" value="10"/>
</com.ibm.ejs.models.base.extensions.webappext:WebAppExtension>
So as per servlet3 and Websphere 8.5.5.8, I change to replace above two .xmi files with ibm-web-bnd.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-bnd xmlns="http://websphere.ibm.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://websphere.ibm.com/xml/ns/javaee http://websphere.ibm.com/xml/ns/javaee/ibm-web-bnd_1_0.xsd" version="1.0">
<virtual-host name="default_host"/>
<resource-ref name="AffinaDataSourceAlias_pma" binding-name="AffinaDataSource_pma"/>
</web-bnd>
and then while installing application on Websphere 8.5.5.8, it use to throw outofmemmory error, so to fix that I change below max memory parameter from 256m to 512m in wsadmin.bat
C:\WebSphere858\AppServer\bin\wsadmin.bat
set PERFJAVAOPTION=-Xms256m -Xmx512m -Xquickstart
Hope this helps.

How does the `getServletMapping()` affects the URL in Spring WebMVC? [duplicate]

I have a web.xml file with content:
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>org.mycompany.test1</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet2</servlet-name>
<servlet-class>org.mycompany.test2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>/path/test</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<url-pattern>/path/test/*</url-pattern>
</servlet-mapping>
I tried requests
.../path/test/abc
.../path/test
Both requests are processed by Servlet2. Why?
UPDATE
Thank you guys for your help.
I realised that behaviour depends on order of servlet-mapping declaration.
I tried this web.xml
<servlet>
<servlet-name>servlet1</servlet-name>
<servlet-class>org.mycompany.test1</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet2</servlet-name>
<servlet-class>org.mycompany.test2</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet3</servlet-name>
<servlet-class>org.mycompany.test3</servlet-class>
</servlet>
<servlet>
<servlet-name>servlet4</servlet-name>
<servlet-class>org.mycompany.test4</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servlet1</servlet-name>
<url-pattern>/path/test</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet2</servlet-name>
<url-pattern>/path/test/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet3</servlet-name>
<url-pattern>/path/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>servlet4</servlet-name>
<url-pattern>/path</url-pattern>
</servlet-mapping>
results:
.../path/test/abc - servlet2
.../path/test/ - servlet2
.../path/test - servlet2
.../path/abc - servlet3
.../path/ - servlet4
.../path - servlet4
From Servlet 3.0 specification, this is how the web container must locate the servlet after receiving a request (emphasis mine):
The path used for mapping to a servlet is the request URL from the
request object minus the context path and the path parameters. The
URL path mapping rules below are used in order. The first successful
match is used with no further matches attempted:
The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the
servlet.
The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory
at a time, using the ’/’ character as a path separator. The longest
match determines the servlet selected.
If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles
requests for the extension. An extension is defined as the part of
the last segment after the last ’.’ character.
If neither of the previous three rules result in a servlet match, the container will attempt to serve content appropriate for the
resource requested. If a "default" servlet is defined for the
application, it will be used. Many containers provide an implicit
default servlet for serving content.
The container must use case-sensitive string comparisons for matching.
You should also look the specification of mappings (given below):
In the Web application deployment descriptor, the following syntax is
used to define mappings:
A string beginning with a ‘/’ character and ending with a ‘/*’ suffix
is used for path mapping.
A string beginning with a ‘*.’ prefix is used as an extension mapping.
The empty string ("") is a special URL pattern that exactly maps to
the application's context root, i.e., requests of the form
http://host:port/<contextroot>/. In this case the path info is ’/’ and
the servlet path and context path is empty string (““).
A string containing only the ’/’ character indicates the "default"
servlet of the application. In this case the servlet path is the
request URI minus the context path and the path info is null.
All other strings are used for exact matches only
Let us look at examples now. Consider the following set of mappings:
Path Pattern Servlet
/foo/bar/* servlet1
/baz/* servlet2
/catalog servlet3
*.bop servlet4
The following behavior would result:
Incoming Path Servlet Handling Request
/foo/bar/index.html servlet1
/foo/bar/index.bop servlet1
/baz servlet2
/baz/index.html servlet2
/catalog servlet3
/catalog/index.html “default” servlet
/catalog/racecar.bop servlet4
/index.bop servlet4
Note that in the case of /catalog/index.html and /catalog/racecar.bop, the
servlet mapped to “/catalog” is not used because the match is not exact.
Now coming to your problem :)
/path/test belongs to the 5th point of specification of mappings. Which means only the paths ending in /path/test will target servlet1.
However /path/test/* qualifies for the first point of the same specification. This means that:
.../path/test will be handled by servlet1 and
.../path/test/abc will be handled by servlet2
Which was verified by me in a test application.
Your paths conflict.
Both of your paths mean the same, the '/*' doesn't make any difference.
Apparently then when you try your path, the last match (servlet2) is executed.
You usually put in a path with the Servlet name, like for example:
/path/test/servlet1
/path/test/servlet2

ServletContextListener not being invoked

I creating a Java EE 7 project using Eclipse Maven plugin. My problem is when I run the application the class that implements SerlvetContextListener does not get invoked. What is causing this problem?
#WebListener
public class ApplicationContextListener implements ServletContextListener{
#Override
public void contextInitialized(ServletContextEvent sce)
{
Request request = new HttpRequest(sce);
new Thread (request).start();
HibernateUtil.getSessionFactory();
}
#Override
public void contextDestroyed(ServletContextEvent sce)
{
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<listener>com.kyrogaming.AppServletContextListener</listener>
<!-- Jersey Mapping -->
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.kyrogaming.webservices</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
<!-- end Jersey Mapping -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
To summarize JNL's and Ted Goddard's answers:
For a ServletContextListener (or other listeners, such as a ServletContextAttributeListener or a ServletRequestAttributeListener) to be loaded by the servlet container, you need to tell the container about it. As described in the API docs, there are three ways to do this:
Declare it in the deployment descriptor (web.xml):
com.kyrogaming.AppServletContextListener
or annotate its class with #WebListener (see "Note about annotations" below)
or register it programatically, via the methods in ServletContext, such as addListener().
Note about annotations
Method 1) and 3) will always work. For method 2) (annotations) to work, the servlet container must be configured to scan the classes in the classpath, to find the annotated listener classes.
The webapp's own classes (under WEB-INF/classes) and libraries (JARs under WEB-INF/lib) will not be scanned if the web.xml contains the attribute metadata-complete="true" (the attribute defaults to false). See the Java Servlet Specification Version 3.0, chapter 8.1, "Annotations and pluggability".
In a web application, classes using annotations will have their annotations processed only if they are located in the WEB-INF/classes directory, or if they are packaged in a jar file located in WEB-INF/lib within the application. The web application deployment descriptor contains a new “metadata-complete” attribute on the web-app element. The “metadata-complete” attribute defines whether the web descriptor is complete, or whether the class files of the jar file should be examined for annotations and web fragments at deployment time. If “metadata-complete” is set to "true", the deployment tool MUST ignore any servlet annotations present in the class files of the application and web fragments. If the metadata-complete attribute is not specified or is set to "false", the deployment tool must examine the class files of the application for annotations, and scan for web fragments.
So, to allow the container to find annotated classes in JARs, make sure the web.xml sets metadata-complete="false", or does not set it at all.
Note that setting this may delay the application startup; see for example What to do with annotations after setting metadata-complete="true" (which resolved slow Tomcat 7 start-up)? .
Unfortunately, that still does not explain why the ServletContextListener in the question is not loaded. Note that the web.xml in the question does not metadata-complete, meaning it defaults to false, thus classpath scanning is enabled. There is probably some other problem; this checklist hopefully helps in finding it.
Using metadata-complete="false" in web.xml fixed this issue for me.
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="false">
In web.xml you also need to specify the <listener-class>.
<listener>
<listener-class>
com.kyrogaming.AppServletContextListener
</listener-class>
</listener>
For the record, I'm adding yet another possible (and rather vicious) cause of ServletContextListener not being invoked.
This can happen when you have a java.lang.LinkageError, that is when you forgot to add <scope>provided</scope> to your javax.servlet-api dependency.
In such a case the listener instance is created but only the static part is executed, not the contextInitialized and contextDestroyed methods.
You shall discover only when you invoke some servlet, as the linkage error is not raised during listener instantiation.
There is one other extremely rare scenario which can cause this. (which took me 4 hours to uncover)
If you're using Tomcat10 then you can't use javax.servlet library in your maven/gradle.
Tomcat9 still has javax.servlet, but Tomcat10 migrated to jakarta.servlet
Tomcat10 expects to have Listener class that uses jakarta.servlet.ServletContextListener
So use this maven dependency: (scope is provided, because Tomcat10 already has such library)
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
The running container might need to explicitly allow scanning for annotations:
Ex for jetty:
cd [JETTY_BASE]
java -jar [JETTY_HOME]/start.jar --add-module=annotations
In a Spring-Boot 1.3+ scenario, you need to have the package for the class annotated with #WebListener (and #WebFilter, #WebServlet) fall under the #ServletComponentScan package scope.
Per Baeldung.

Unable to load spring application Context

I want to load myapp-servlet.xml as my web application context. I have neither defined spring contextLoaderListner not have defined context params, only the dispatcher servlet is defined.
<display-name>myapp</display-name>
<description/>
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
config file is present in /WEB-INF/myapp-servlet.xml and I'm expecting it to be loaded. But I'm getting the following exception
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/applicationContext.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext.xml]
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:341)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302)
I'm want to know why the default applicationContext.xml file is looked for even when context-param and contextLoaderListener is not defined?
Just add the appropriate context-param for your Spring config file:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/myapp-servlet.xml</param-value>
</context-param>
There is probably a default config file location in the Spring code... Seems like a good opportunity to UTSL...
Just add
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
to your web.xml

What is causing the mapping failure in this Spring MVC app?

I'm trying to get a very simple Spring #mvc app to work and I'm running into what appears to be a mapping error.
From web.xml:
<servlet>
<servlet-name>works</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>works</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
The controller:
#Controller
#RequestMapping("/test")
public class TESTController {
private static Logger appLogger = Logger.getLogger("AppLogFile");
public String serviceRequest( Model model)
{
appLogger.info("======================= TESTController GET ===============================");
model.addAttribute("returnString","TESTController handled the request") ;
return "SingleStringView";
}
works-servlet.xml:
<context:component-scan base-package="com.ami.dbconnect.controller" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<bean id="TESTController" class="com.ami.dbconnect.controller.TESTController"></bean>
<!-- view resolver not shown -->
The app is deployed to Tomcat 7 at /webapps/works. The Tomcat file structure is:
webapps
/works
/WEB-INF
/classes
/lib
I'm trying to invoke the controller with the url: localhost:8080/works/test
In tomcat7-stdout I see:
1106 [pool-2-thread-1] DEBUG org.springframework.web.servlet.DispatcherServlet - Published WebApplicationContext of servlet 'works' as ServletContext attribute with name [org.springframework.web.servlet.FrameworkServlet.CONTEXT.works]
1106 [pool-2-thread-1] INFO org.springframework.web.servlet.DispatcherServlet - FrameworkServlet 'works': initialization completed in 728 ms
1106 [pool-2-thread-1] DEBUG org.springframework.web.servlet.DispatcherServlet - Servlet 'works' configured successfully
14068 [http-apr-8080-exec-2] DEBUG org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'works' processing GET request for [/works/test]
14071 [http-apr-8080-exec-2] WARN org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/works/test] in DispatcherServlet with name 'works'
So my question (finally!):
Is Spring not recognizing the annotations in the controller? If not, what could be wrong in this simple set up?
Thanks for any help or advice,
beeky
You need to move #RequestMapping annotation to your serviceRequest method.
#RequestMapping at class level may be used to specify common path prefix for all #RequestMapping-annotated of that class, but it doesn't take any effect without annotations at method level.
Maybe problem is in the servlet mapping section?
Try to change:
<servlet-mapping>
<servlet-name>works</servlet-name>
<url-pattern>/works/*</url-pattern>
</servlet-mapping>
And try again.

Resources