Embedded tomcat 7 servlet 3.0 anotaciones no funcionan

Tengo un proyecto de prueba simplificado que contiene una versión de Servlet 3.0, declarada con anotaciones como esta:

@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(); } } 

También tengo un archivo web.xml como ese:

   testServlet g1.TestServlet   testServlet /testWebXml   

Intenté hacer una prueba JUnit usando Embedded Tomcat 7. Cuando inicio el Embedded Tomcat, solo puedo acceder al servlet a través del url-pattern declarado en web.xml (/ testWebXml). Si bash acceder a él a través del url-pattern declarado a través de la anotación (/ test), se encuentra la página 404 no encontrada.

Aquí está el código para mi prueba:

  String webappDirLocation = "src/main/webapp/"; Tomcat tomcat = new Tomcat(); tomcat.setPort(8080); tomcat.addWebapp("/jerseyTest", new File(webappDirLocation).getAbsolutePath()); tomcat.start(); tomcat.getServer().await(); 

Solo para asegurarme de haber configurado correctamente mi proyecto, también instalé un Tomcat 7 real y desplegué la guerra. Esta vez, tanto la url web.xml declarada como la url de anotación para mi servlet funcionan bien.

Entonces mi pregunta es: ¿alguien sabe cómo hacer que Embedded Tomcat 7 tenga en cuenta mis anotaciones de Servlet 3.0?

También debo decir que es un proyecto de Maven, y pom.xml contiene las siguientes dependencias:

   org.apache.tomcat tomcat-catalina 7.0.29 test   org.apache.tomcat.embed tomcat-embed-core 7.0.29 provided   org.apache.tomcat tomcat-jasper 7.0.29 test   junit junit 4.8.1 test  

== ACTUALIZACIÓN ==

Aquí hay un problema que parece similar a esto (excepto que la anotación de Servlet 3.0 que no funciona está en Listener, no Servlet), que tiene una solución sugerida:

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

Lo probé y no funcionó:

Cambió el código de inicio de Tomcat incrustado a:

 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(); 

Otras cosas que he intentado, también sin éxito:

  • especificando metadata-complete = “false” en la etiqueta web.xml “web-app”

  • actualizando las dependencias de Maven a la versión 7.0.30

  • depurando la clase org.apache.catalina.startup.ContextConfig . Hay un código allí que busca anotaciones de @WebServlet , es solo que nunca se ejecuta (línea 2115). Esta puede ser una buena manera de llegar a la raíz del problema, pero la clase es bastante grande, y no tengo tiempo para hacer esto ahora. Tal vez si alguien estaría dispuesto a ver cómo funciona esta clase, y bajo qué condiciones (parámetros de configuración) logra verificar correctamente las clases de su proyecto para esa anotación, podría obtener una respuesta válida.

Bueno, finalmente lo resolví buscando en las fonts de Tomcat7, concretamente en las pruebas unitarias que tratan con EmbeddedTomcat y las anotaciones de servlet 3.0.

Básicamente, debe iniciar su Embedded Tomcat 7 de esta manera para que tenga conocimiento de sus clases anotadas:

 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(); 

En aras de la claridad, debo mencionar que esto funciona para un proyecto Maven estándar donde sus “recursos web” (como páginas estáticas y dinámicas, directorio WEB-INF, etc.) se encuentran en:

[el directorio raíz de su proyecto] / src / main / webapp

y tus clases se comstackn en

[directorio raíz de su proyecto] / target / classes

(tal que tendría [el directorio raíz de su proyecto] / target / classes / [algún paquete] /SomeCompiledServletClass.class)

Para otros diseños de directorios, estas ubicaciones deben cambiarse en consecuencia.

==== ACTUALIZACIÓN: Embedded Tomcat 8 ====

Gracias a @kwak por notar esto.

Las API han cambiado un poco, aquí cómo cambia el ejemplo anterior cuando se usa 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(); 
  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(); 

funciona bien para mí usando Tomcat Embed 7.0.90 de MVN

Dada la magia JNDI en el fragmento que publicaste:

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

¿podría usted configurar el mismo tipo de entradas en JNDI usted mismo?

 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);