¿Cómo configurar la aplicación JAX-RS usando solo anotaciones (no web.xml)?

80

¿Es posible configurar una aplicación JAX-RS usando solo anotaciones? (usando Servlet 3.0 y JAX-RS Jersey 1.1.0)

Lo intenté y no tuve suerte. web.xmlParece necesario usar algunos .


Configuración A (funcionando, pero tiene configuración web.xml)

web.xml

   ...
   <servlet>
      <servlet-name>org.foo.rest.MyApplication</servlet-name>
   </servlet>
   <servlet-mapping>
       <servlet-name>org.foo.rest.MyApplication</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>
   ...

Java

@ApplicationPath("/")
public class MyApplication extends Application {
    ...
}

Configuración B (no funciona, se lanzó una excepción)

@ApplicationPath("/")
@WebServlet("/*") // <-- 
public class MyApplication extends Application {
    ...
}

Este último parece insistir en que la Aplicación será una subclase de Servlet (la excepción no deja conjeturas)

java.lang.ClassCastException: org.foo.rest.MyApplication cannot be cast to javax.servlet.Servlet

Preguntas

  1. ¿Por qué funcionó la definición web.xml pero no la anotación? ¿Cual es la diferencia?

  2. ¿Hay alguna manera de que funcione, por ejemplo, tener una aplicación JAX-RS sin web.xml?

Eran Medan
fuente
1
Si puede probar con NetBeans, hay un asistente para crear servicios web RESTFul. Parece que lo que está intentando hacer es lo que hace este asistente en la versión 6.8. Estoy usando 7.0.1 y el nuevo enfoque es más simple pero usa un solo servlet para el propósito, que es com.sun.jersey.spi.container.servlet.ServletContainer pero 'definido en web.xml
perissf

Respuestas:

168

** POR FAVOR LEA SI UTILIZA TOMCAT O JETTY! **

La respuesta aceptada hace el trabajo, pero sólo si la aplicación Web se implementa en un servidor de aplicación como Glassfish o JBoss, y posiblemente contenedores de servlets con extensiones de EE como TomEE. Que no funciona en contenedores de servlets estándar como Tomcat, que estoy seguro que la mayoría de las personas que buscan una solución a este problema que desee utilizar.

Si está utilizando una instalación estándar de Tomcat (o algún otro contenedor de servlets), debe incluir una implementación REST ya que Tomcat no viene con una. Si está usando Maven, agregue esto a la dependenciessección:

<dependencies>
  <dependency>
    <groupId>org.glassfish.jersey.bundles</groupId>
    <artifactId>jaxrs-ri</artifactId>
    <version>2.13</version>
  </dependency>
  ...
</dependencies>

Luego, simplemente agregue una clase de configuración de la aplicación a su proyecto. Si no tiene ninguna necesidad de configuración especial además de establecer la ruta de contexto para los demás servicios, la clase puede estar vacía. Una vez que se agrega esta clase, no es necesario configurar nada en web.xml(o tener uno):

package com.domain.mypackage;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("rest") // set the path to REST web services
public class ApplicationConfig extends Application {}

Después de esto, declarar sus servicios web es sencillo usando las anotaciones JAX-RS estándar en sus clases de Java:

package com.domain.mypackage;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.MatrixParam;
import javax.ws.rs.Path;

// It's good practice to include a version number in the path so you can have
// multiple versions deployed at once. That way consumers don't need to upgrade
// right away if things are working for them.
@Path("calc/1.0")
public class CalculatorV1_0 {
  @GET
  @Consumes("text/plain")
  @Produces("text/plain")
  @Path("addTwoNumbers")
  public String add(@MatrixParam("firstNumber") int n1, @MatrixParam("secondNumber") int n2) {
    return String.valueOf(n1 + n2);
  }
}

Esto debería ser todo lo que necesita. Si su instalación de Tomcat se está ejecutando localmente en el puerto 8080 e implementa su archivo WAR en el contexto myContext, vaya a ...

http://localhost:8080/myContext/rest/calc/1.0/addTwoNumbers;firstNumber=2;secondNumber=3

... debería producir el resultado esperado (5).

Alvin Thompson
fuente
6
@JavaDude: debe configurar Maven para generar el archivo WAR incluso si web.xmlno está presente. En su pom.xml, en "Crecimiento> plugins", ADD (si no está ya allí): <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>2.3</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin>.
Alvin Thompson
6
@JavaDude: la parte importante es la <failOnMissingWebXml>false</fai‌​lOnMissingWebXml>opción.
Alvin Thompson
3
@JavaDude: si no es así, es probable que no haya creado una clase que se extienda Applicationy le agregue la @ApplicationPathanotación. La clase puede estar vacía, pero debe estar allí.
Alvin Thompson
3
Es suficiente agregar solo jersey-container-servlet, sin eliminar toda la jaxrs-ridependencia. Por ejemplo, en mi caso fue 'org.glassfish.jersey.core:jersey-server:2.18'+ 'org.glassfish.jersey.containers:jersey-container-servlet:2.18'.
leveluptor
3
Dado que la pregunta está claramente etiquetada con "java-ee", no entiendo la confusión acerca de proporcionar y votar una respuesta que NO aborde un entorno jee. -1
Jan Galinski
51

Parece que todo lo que necesitaba hacer es esto (Servlet 3.0 y superior)

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/*")
public class MyApplication extends Application {
    ...
}

Y aparentemente no se necesitaba ninguna configuración web.xml (probado en Tomcat 7)

Eran Medan
fuente
1
¿No entiendes cómo funciona esto, puedes usar solo la anotación sin ninguna otra configuración? He intentado así y todavía no funciona ...
Filipe
2
@Filipe: Supongo que estás usando Tomcat o algún otro contenedor de servlets. En realidad, esto no funciona en contenedores de servlets como Tomcat, solo funciona con servidores de aplicaciones como Glassfish o Wildfly, y tal vez TomEE. Si está utilizando Tomcat, vea mi respuesta (tardía) a continuación.
Alvin Thompson
8
¡Esta respuesta NO funciona para Tomcat o Jetty! Consulte mi respuesta para que todo funcione en esos (y otros) contenedores de servlets.
Alvin Thompson
1
Dependiendo de su aplicación, usted no necesita un web.xml, aunque será suficiente una vacía. Ver docs.jboss.org/resteasy/docs/3.0.9.Final/userguide/html_single/…
Benjamin Maurer
Esto funciona en WebSphere Liberty pero no en WebSphere 8.xy 9.0 Classic.
Archimedes Trajano
15

El Capítulo 2 de la especificación JAX-RS: Java ™ API para RESTful Web Services describe el proceso de publicación de una aplicación JAX-RS en un entorno Servlet (sección 2.3.2 Servlet en la especificación).

Tenga en cuenta que solo se recomienda el entorno Servlet 3 (sección 2.3.2 Servlet, página 6):

Se RECOMIENDA que las implementaciones sean compatibles con el mecanismo de conectabilidad del marco de trabajo Servlet 3 para permitir la portabilidad entre contenedores y aprovechar las funciones de escaneo de clases proporcionadas por el contenedor.

En resumen, si desea utilizar un enfoque no-web.xml, es posible con una implementación personalizada de javax.ws.rs.core.Application que registra los recursos del servicio RESTful con la anotación javax.ws.rs.ApplicationPath .

@ApplicationPath("/rest")

Aunque preguntó específicamente sobre Jersey, también puede leer el artículo Implementación de servicios RESTful con JAX-RS y WebSphere 8.5 Liberty Profile en el que describí el proceso de publicación no-web.xml para WebSphere Liberty Profile (con Apache Wink como la implementación de JAX-RS).

Jacek Laskowski
fuente
1
¡Gracias! Tengo otra pregunta fuertemente relacionada que me está volviendo loco, ¿hay alguna manera de tener la ruta en la raíz (por ejemplo, @ApplicationPath ("/ *")) y aún servir JSP usando solo anotaciones? (aunque incluso con la configuración web.xml no pude hacer que sucediera) - la pregunta está aquí: stackoverflow.com/questions/10874188/… , ya que parece que conoce bastante bien JAX-RS, ¿podría echar un vistazo? :) quizás veas lo que me estoy perdiendo ... ¡gracias!
Eran Medan
No lo sé. Los instintos me dicen que debería funcionar sin configuración adicional, ya que el proceso de resolución de un recurso para manejar una solicitud es de la coincidencia exacta con los patrones de URL. Echaré un vistazo y responderé. ¡Gracias por animarme a ampliar mis conocimientos! :-)
Jacek Laskowski
Estoy usando @ApplicationPath("")para tener mis servicios REST en la raíz (en relación con la raíz de contexto de la aplicación web) en JBoss EAP 7. No tengo beans.xmlni web.xml.
Julien Kronegg
7

Necesita configurar las dependencias correctas en pom.xml

<dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
    </dependency>

Más detalles aquí: ejemplo de inicio para jax-rs

ACV
fuente
¿Puedo eliminar web.xml si se utiliza ResourceConfig / Application?
BAE
sí, pero debe agregar esto en pom.xml i fusnig mave:<failOnMissingWebXml>
ACV
Gracias. pero, ¿hay algo que web.xml pueda hacer pero ResourceConfig no?
BAE
No, creo que, dado que la especificación de servlet 3.0, web.xml ya no es necesaria. Pero a veces es más conveniente usar web.xml ya que no requiere recompilación si desea cambiar algo.
ACV
¿Y si no estás usando Maven ? Mi proyecto es demasiado grande en este momento para retroceder.
TheRealChx101
1

Las dependencias mencionadas anteriormente no funcionaron para mí. De la guía del usuario de Jersey:

Jersey proporciona dos módulos Servlet. El primer módulo es el módulo de Servlet de núcleo de Jersey que proporciona el soporte de integración de Servlet principal y se requiere en cualquier contenedor de Servlet 2.5 o superior:

<dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet-core</artifactId>
</dependency>

Para admitir modos de implementación adicionales de Servlet 3.xy el modelo de programación de recursos JAX-RS asíncrono, se requiere un módulo Jersey adicional:

<dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet</artifactId>
</dependency>

El módulo jersey-container-servlet depende del módulo jersey-container-servlet-core, por lo tanto, cuando se usa, no es necesario declarar explícitamente la dependencia jersey-container-servlet-core.

https://jersey.github.io/documentation/latest/deployment.html#deployment.servlet.3

bzak
fuente
0

Como señaló @ Eran-Medan, JBoss EAP 7.1 (tenga en cuenta que sin una aplicación web, por lo que no hay servlet, lo estaba haciendo en un proyecto EJB 3.2) tuve que agregar el atributo "valor" como tal, estaba obteniendo una excepción que el El atributo de valor era obligatorio.

Esto funcionó para mi

    @ApplicationPath(value="/*")
        public class MyApplication extends Application {

            private Set singletons = new HashSet();

            public MyApplication() {
                singletons.add(new MyService());
            }

            ...
    }

Seguimiento de pila

    Caused by: java.lang.annotation.IncompleteAnnotationException: javax.ws.rs.ApplicationPath missing element value
        at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:80)
        at com.sun.proxy.$Proxy141.value(Unknown Source)
        ... 21 more
JGlass
fuente