@WebServlet annotation doesn't work with Tomcat 8
Asked Answered
S

5

15

I want to use the @WebServlet annotation in a Java EE webapp which runs on Tomcat 8.

I have read that I need to declare Servlet Version 3.1 in my web.xml and that my Servlet needs to extend HttpServlet. I did all that but still the @WebServlet doesn't work. I am getting a HTTP 404.

I also tried my configuration with metadata-complete="false" in my web.xml, but still no success.

Here is my web.xml and Servlet.

The complete sample code can be found on GitHub.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
  version="3.1" 
  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">

  <context-param>
    <param-name>facelets.DEVELOPMENT</param-name>
    <param-value>true</param-value>
  </context-param>

  <!-- https://mcmap.net/q/541136/-hot-republishing-deploying-of-static-xhtml-files-issues -->
  <!-- Put "-1" to disable this feature -->
  <context-param>
    <param-name>facelets.REFRESH_PERIOD</param-name>
    <param-value>1</param-value>
  </context-param>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.xhtml</url-pattern>
  </servlet-mapping>

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

  <!-- JSF -->
  <listener>
    <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>
  </listener>   

  <!-- CDI -->
  <listener>
    <listener-class>org.apache.webbeans.servlet.WebBeansConfigurationListener</listener-class>
  </listener>

</web-app>

TestServlet.java

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet(name = "TestServlet", urlPatterns = {"*.serve"})
public class TestServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
          throws ServletException, IOException {
    try (ServletOutputStream out = resp.getOutputStream()) {
      out.write("Hello World".getBytes());
      out.flush();
    }
  }

}
Staple answered 28/9, 2014 at 21:35 Comment(0)
S
25

I got it working. I had to extend the way I started my Tomcat 8.0.12 server.

Nevertheless, there are three main things that must be done:

  1. web-app version in web.xml has to be at least 3.0 (I used 3.1)
  2. metadata-complete in web.xml may not be true (default is "false")
  3. classes directories have to be added to the embedded Tomcat before start

Here is an example for the web.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<web-app 
  version="3.1" 
  metadata-complete="false"  
  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">

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

</web-app>

This is how my Tomcat main class looks like (which supports now @WebServlet annotations):

public static void main(String[] args) throws Exception {
  String contextPath = "/";
  String webappDirLocation = "src/main/webapp/";
  String baseDirectory = new File(webappDirLocation).getAbsolutePath();

  Tomcat tomcat = new Tomcat();
  tomcat.setPort(8080);
  StandardContext context = (StandardContext) tomcat.addWebapp(contextPath, baseDirectory);

  // Additions to make @WebServlet work
  String buildPath = "target/classes";
  String webAppMount = "/WEB-INF/classes";

  File additionalWebInfClasses = new File(buildPath);
  WebResourceRoot resources = new StandardRoot(context);
  resources.addPreResources(new DirResourceSet(resources, webAppMount, additionalWebInfClasses.getAbsolutePath(), contextPath));
  context.setResources(resources);
  // End of additions

  tomcat.start();
  tomcat.getServer().await();
}
Staple answered 7/10, 2014 at 23:41 Comment(3)
This works sometimes, but other times leads to a mysterious exception.Misericord
See also Embedded tomcat 7 servlet 3.0 annotations not workingCaffey
If tomcat discovers a context.xml it will use it to override the one you are programmatically setting . Makes sure to set context.setOverride(true); github.com/apache/tomcat/blob/main/java/org/apache/catalina/…Montagnard
E
1

You might want to change the lib you're using, like from javax to jakarta

<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.0.0</version>
    <scope>provided</scope>
</dependency>

Then change all javax.* imports to jakarta.*

Everybody answered 14/10, 2022 at 17:18 Comment(1)
This was the solution for me! I use Tomcat 10.Awl
T
0

Check out your javax.servlet version.

Make sure it's 3 or plus.

If not, change it to the latest one.

<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>
Thea answered 10/6, 2022 at 18:57 Comment(0)
D
0

Using @WebServlet<"/name"> instead of @WebServlet<"name"> might work.

Discussion answered 2/6, 2023 at 20:33 Comment(0)
A
-2

How about @ServletComponentScan?

Example from http://www.baeldung.com/spring-servletcomponentscan

@ServletComponentScan
@SpringBootApplication
public class SpringBootAnnotatedApp {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootAnnotatedApp.class, args);
    }
}
Armistice answered 26/7, 2018 at 13:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.