Shivan Dragon
Shivan Dragon

Reputation: 15219

Embedded tomcat 7 servlet 3.0 annotations not working

I have a stripped down test project which contains a Servlet version 3.0, declared with annotations like so:

    @WebServlet("/test")
public class TestServlet extends HttpServlet {

    private static final long serialVersionUID = -3010230838088656008L;

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException{
        response.getWriter().write("Test");
        response.getWriter().flush();
        response.getWriter().close();
    }
}

I also have a web.xml file like so:

<web-app 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_3_0.xsd"
      version="3.0">

      <servlet>
        <servlet-name>testServlet</servlet-name>
        <servlet-class>g1.TestServlet</servlet-class>
      </servlet>      

      <servlet-mapping>
        <servlet-name>testServlet</servlet-name>
        <url-pattern>/testWebXml</url-pattern>
      </servlet-mapping>

</web-app> 

I've tried to make a JUnit test using Embedded Tomcat 7. When I start the Embedded Tomcat I can only access the servlet via the url-pattern declared in web.xml (/testWebXml). If I try to access it via the url-pattern declared via annotation (/test) it sais 404 page not found.

Here's the code for my test:

    String webappDirLocation = "src/main/webapp/";
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(8080);

    tomcat.addWebapp("/jerseyTest", new File(webappDirLocation).getAbsolutePath());

    tomcat.start();
    tomcat.getServer().await();

Just to make sure I've set up my project correctly, I've also installed an actual Tomcat 7 and deployed the war. This time, both web.xml declared url and annotation url for my servlet work ok.

So my question is: does anyone know how to make Embedded Tomcat 7 take into account my Servlet 3.0 annotations?

I should also state that it's a Maven project, and the pom.xml contains the following dependencies:

    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-catalina</artifactId>
        <version>7.0.29</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>7.0.29</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jasper</artifactId>
        <version>7.0.29</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.1</version>
        <scope>test</scope>
    </dependency>

== UPDATE ==

Here's an issue that seems similar to this (except the Servlet 3.0 annotation that is not working is on Listener, not Servlet), which has a suggested fix:

https://issues.apache.org/bugzilla/show_bug.cgi?id=53903

I've tried it and it didn't work:

Changed the Embedded Tomcat start code to:

String webappDirLocation = "src/main/webapp/";
Tomcat tomcat = new Tomcat();

tomcat.enableNaming();
tomcat.setPort(8080);


Context ctx = tomcat.addWebapp(tomcat.getHost(), "/embeddedTomcat", new File(webappDirLocation).getAbsolutePath());
((StandardJarScanner) ctx.getJarScanner()).setScanAllDirectories(true);

tomcat.start();

tomcat.getServer().await();

Other things I've tried, also without success:

Upvotes: 25

Views: 14392

Answers (4)

tommyhp2
tommyhp2

Reputation: 27

I think in 8.5 and later it's just one liner:

Context.setAddWebinfClassesResources(true);

Sets the flag that indicates if /WEB-INF/classes should be treated like an exploded JAR and JAR resources made available as if they were in a JAR.

http://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/catalina/core/StandardContext.html#setAddWebinfClassesResources(boolean)

Upvotes: -1

Student414
Student414

Reputation: 66

    Tomcat tomcat = new Tomcat();
    tomcat.setPort(port);

    Context ctx = tomcat.addWebapp("/", new File(docBase).getAbsolutePath());
    StandardJarScanner scan = (StandardJarScanner) ctx.getJarScanner();
    scan.setScanClassPath(true);
    scan.setScanBootstrapClassPath(true); // just guessing here
    scan.setScanAllDirectories(true);
    scan.setScanAllFiles(true);

    tomcat.start();
    tomcat.getServer().await();

it works well for me using Tomcat Embed 7.0.90 from MVN

Upvotes: 2

Shivan Dragon
Shivan Dragon

Reputation: 15219

Well I finally solved it by looking in the Tomcat7 sources, namely in the unit tests that deal with EmbeddedTomcat and servlet 3.0 annotations.

Basically, you must start your Embedded Tomcat 7 like this to make it aware of your annotated classes:

String webappDirLocation = "src/main/webapp/";
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);

StandardContext ctx = (StandardContext) tomcat.addWebapp("/embeddedTomcat",
                new File(webappDirLocation).getAbsolutePath());

//declare an alternate location for your "WEB-INF/classes" dir:     
File additionWebInfClasses = new File("target/classes");
VirtualDirContext resources = new VirtualDirContext();
resources.setExtraResourcePaths("/WEB-INF/classes=" + additionWebInfClasses);
ctx.setResources(resources);

tomcat.start();
tomcat.getServer().await();

For the sake of clarity I should mention that this works for a standard Maven project where your "web resources" (such as static and dynamic pages, WEB-INF directory etc) are found in:

[your project's root dir]/src/main/webapp

and your classes get compiled into

[your project's root dir]/target/classes

(such that you'd have [your project's root dir]/target/classes/[some package]/SomeCompiledServletClass.class)

For other directories layouts, these locations need to be changed accordingly.

==== UPDATE: Embedded Tomcat 8 ====

Thanks to @kwak for noticing this.

The APIs have changed a bit, here how the above example changes when using Embedded Tomcat 8:

String webappDirLocation = "src/main/webapp/";
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);

StandardContext ctx = (StandardContext) tomcat.addWebapp("/embeddedTomcat",
                new File(webappDirLocation).getAbsolutePath());

//declare an alternate location for your "WEB-INF/classes" dir:     
File additionWebInfClasses = new File("target/classes");
WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", additionWebInfClasses.getAbsolutePath(), "/"));
ctx.setResources(resources);

tomcat.start();
tomcat.getServer().await();

Upvotes: 40

Ian Roberts
Ian Roberts

Reputation: 122364

Given the JNDI magic in the snippet you posted:

try {
    listBindings = context.getResources().listBindings(
            "/WEB-INF/classes");
} catch (NameNotFoundException ignore) {
    // Safe to ignore
}

could you maybe set up the same sort of entries in JNDI yourself?

Context ctx = tomcat.addWebapp(...);
FileDirContext fdc = new FileDirContext();
fdc.setDocBase(new File("/path/to/my/classes").getAbsolutePath());
ctx.getResources().createSubcontext("/WEB-INF/classes").bind("myclasses", fdc);

Upvotes: 0

Related Questions