Подтвердить что ты не робот

Встроенные 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). Это может быть хорошим способом добраться до корня проблемы, но класс довольно большой, и у меня нет времени для этого. Возможно, если кто-то захочет посмотреть, как работает этот класс, и при каких условиях (параметры конфигурации) он правильно проверяет ваши классы проектов для этой аннотации, он может получить правильный ответ.

4b9b3361

Ответ 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);