javatomcatservletsannotationscatalina

Tomcat Catalina context - add existing servlet to context


I would like to add an existing servlet to a context and it works, when I use (Main.java):

Tomcat.addServlet(ctx, "MyServlet", new MyServlet());
ctx.addServletMappingDecoded("/url_pattern", "MyServlet")

However, I have annotations inside servlet to map url_pattern(MyServlet.java):

@WebServlet(name = "MyServlet", urlPatterns = { "/url_pattern" })
@MultipartConfig(
  fileSizeThreshold = 1024 * 1024 * 1, // 1 MB
  maxFileSize = 1024 * 1024 * 10,      // 10 MB
  maxRequestSize = 1024 * 1024 * 100   // 100 MB
)

Unfortunately, these annotations do not work. I would like to delete mapping from Main.java and use mapping from my Servlet annotation.

I use Tomcat 10.0.0.

Main.java

import java.io.File;

import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;

public class Main {

    public static void main(String[] args) throws LifecycleException,
    InterruptedException {
    Tomcat tomcat = new Tomcat();
    tomcat.setPort(8082);

    Context ctx = tomcat.addContext("", new File(".").getAbsolutePath());

    Tomcat.addServlet(ctx, "MyServlet", new MyServlet());
    
    ctx.setAllowCasualMultipartParsing(true);
    ctx.addServletMappingDecoded("/url_pattern", "MyServlet");
    
    
    tomcat.start();
    tomcat.getConnector();
    }
}

Solution

  • The idea behind embedded Tomcat is to do in code, what the usual Tomcat does through configuration. Therefore the configuration of servlets through annotations is not as straightforward as it is in Tomcat.

    You'll be facing two problems:

    Annotation scanning

    The main difference between Tomcat#addContext and Tomcat#addWebapp is that the latter adds some default web.xml (the jsp and default servlet) and scans for Servlet 3.0 annotations and ServletContainerInitializers.

    If you don't need or don't want the default web.xml configuration, you can obtain the same thing with:

    Context ctx = tomcat.addContext("", new File(".").getAbsolutePath());
    ctx.addLifecycleListener(new ContextConfig());
    

    Resources

    Even after enabling annotation scanning Tomcat will not find your servlets, because it looks under /WEB-INF/classes of your docBase (which you set to be the current folder). This is a probably empty folder. If you want Tomcat to scan also the classes in your application you'll need to:

    1. Find the JAR file or directory that contains your Main class,
    2. Mount the contents of that JAR to the /WEB-INF/classes (virtual) folder of your application (cf. Tomcat resources):
    public static void main(String[] args) throws LifecycleException, InterruptedException {
       ...
       // Add the JAR/folder containing this class to PreResources
       final WebResourceRoot root = new StandardRoot(ctx);
       final URL url = findClassLocation(Main.class);
       root.createWebResourceSet(ResourceSetType.PRE, "/WEB-INF/classes", url, "/");
       ctx.setResources(root);
       ...
    }
    
    /*
     * Tries to find the URL of the JAR or directory containing {@code clazz}.
     */
    private static URL findClassLocation(Class< ? > clazz) {
        return clazz.getProtectionDomain().getCodeSource().getLocation();
    }
    

    Summary

    At the end you'll end up with:

        public static void main(String[] args) throws LifecycleException {
            Tomcat tomcat = new Tomcat();
            tomcat.setPort(8082);
            final Context ctx = tomcat.addContext("", Paths.get(".").toAbsolutePath().toString());
            // Add the standard ContextConfig, which scans for annotations
            ctx.addLifecycleListener(new ContextConfig());
            // Add the JAR/folder containing this class to PreResources
            final WebResourceRoot root = new StandardRoot(ctx);
            final URL url = findClassLocation(Main.class);
            root.createWebResourceSet(ResourceSetType.PRE, "/WEB-INF/classes", url, "/");
            ctx.setResources(root);
            // Run Tomcat
            tomcat.getConnector();
            tomcat.start();
            tomcat.getServer().await();
        }