Issue
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 ServletContainerInitializer
s.
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:
- Find the JAR file or directory that contains your
Main
class, - 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();
}
Answered By - Piotr P. Karwasz
Answer Checked By - Pedro (JavaFixing Volunteer)