Встроенные tomcat 7 сервлета 3.0 аннотации не работают
У меня есть тестовый проект, который содержит версию Servlet 3.0, объявленную с аннотациями вроде:
@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();
}
}
У меня также есть файл web.xml, например:
<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>
Я попытался выполнить JUnit-тест с помощью Embedded Tomcat 7. Когда я запускаю Embedded Tomcat, я могу получить доступ к сервлету только через url-шаблон, объявленный в web.xml(/testWebXml). Если я попытаюсь получить к нему доступ через url-шаблон, объявленный с помощью аннотации (/test), он не найден на странице 404.
Вот код для моего теста:
String webappDirLocation = "src/main/webapp/";
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.addWebapp("/jerseyTest", new File(webappDirLocation).getAbsolutePath());
tomcat.start();
tomcat.getServer().await();
Чтобы убедиться, что я правильно настроил свой проект, я также установил фактический Tomcat 7 и развернул войну. На этот раз как web.xml объявленный url, так и аннотирование url для моего сервлета работают нормально.
Итак, мой вопрос: кто-нибудь знает, как заставить Embedded Tomcat 7 учитывать мои аннотации сервлета 3.0?
Я также должен указать, что это проект Maven, а pom.xml содержит следующие зависимости:
<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 ==
Здесь проблема, которая кажется подобной (кроме аннотации Servlet 3.0, которая не работает, находится на Listener, а не на Servlet), у которой есть предлагаемое исправление:
https://issues.apache.org/bugzilla/show_bug.cgi?id=53903
Я пробовал и не работал:
Изменен начальный код встроенного Tomcat:
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();
Другие вещи, которые я пробовал, также безуспешно:
-
настройка метаданных-complete = "false" в теге web.xml "web-app"
-
обновление зависимостей Maven до версии 7.0.30
-
отладка класса org.apache.catalina.startup.ContextConfig
. Там есть код, который проверяет аннотации @WebServlet
, он просто не запускается (строка 2115). Это может быть хорошим способом добраться до корня проблемы, но класс довольно большой, и у меня нет времени для этого. Возможно, если кто-то захочет посмотреть, как работает этот класс, и при каких условиях (параметры конфигурации) он правильно проверяет ваши классы проектов для этой аннотации, он может получить правильный ответ.
Ответы
Ответ 1
Ну, я, наконец, решил его, посмотрев в источниках Tomcat7, а именно в модульных тестах, которые касаются аннотаций EmbeddedTomcat и сервлета 3.0.
В принципе, вы должны запустить свой Embedded Tomcat 7, чтобы он знал о ваших аннотированных классах:
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();
Для ясности я должен упомянуть, что это работает для стандартного проекта Maven, где ваши "веб-ресурсы" (такие как статические и динамические страницы, каталог WEB-INF и т.д.) находятся в:
[ваш корневой каталог проекта]/src/main/webapp
и ваши классы скомпилируются в
[ваш корневой каталог проекта]/цель/классы
(чтобы у вас был [ваш корневой каталог проекта]/target/classes/[some package]/SomeCompiledServletClass.class)
Для других макетов каталогов эти места необходимо соответствующим образом изменить.
==== UPDATE: Embedded Tomcat 8 ====
Спасибо @kwak за это.
API немного изменились, вот как приведенный выше пример изменяется при использовании 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();
Ответ 2
Учитывая магию JNDI в опубликованном фрагменте:
try {
listBindings = context.getResources().listBindings(
"/WEB-INF/classes");
} catch (NameNotFoundException ignore) {
// Safe to ignore
}
Возможно, вы сами можете настроить те же записи в JNDI?
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);