Preloader image

Definir un servicio REST es bastante fácil, simplemente agregue la anotación @Path a una clase luego defina en los métodos el método HTTP a usar (@GET, @POST, …).

El Código

El servicio REST: @Path, @Produces, @Consumes

Aquí vemos un bean que usa las anotaciones @Path, @Produces y @Consumes. La anotación @Path identifica la plantilla de ruta de URI a la que responde el recurso y se especifica en el nivel de clase de un recurso. El valor de la anotación @Path es una plantilla de ruta de URI parcial relativa al URI base del servidor en el que se implementa el recurso, la raíz de contexto del WAR y el patrón de URL al que responde el servlet auxiliar de Jersey. La anotación @Produces se utiliza para especificar los tipos de medios MIME o las representaciones que un recurso puede producir y enviar al cliente. Si @Produces se aplica en el nivel de clase, todos los métodos de un recurso pueden producir los tipos MIME especificados de forma predeterminada. Si se aplica a nivel de método, sobrescribe cualquier @Produces anotaciones aplicadas a nivel de clase. La anotación @Consumes se utiliza para especificar qué tipos de representaciones de medios MIME puede aceptar o consumir un recurso del cliente. Si se aplica @Consumes a nivel de clase, todos los métodos de respuesta aceptan los tipos MIME especificados de forma predeterminada. Si @Consumes se aplica a nivel de método, anula cualquier @Consumes anotaciones aplicadas a nivel de clase.

En realidad líneas:

@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })

son opcionales ya que es la configuración predeterminada. Y estas líneas pueden configurarse también por método si necesita ser más preciso.

@Path("/greeting")
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public class GreetingService {
    @GET
    public Response message() {
        return new Response("Hi REST!");
    }

    @POST
    public Response lowerCase(final Request message) {
        return new Response(message.getValue().toLowerCase());
    }
}

Pruebas

Prueba del servicio JAXRS

La prueba usa Arquillian con el adaptador TomEE embedded, así el servicio REST se ejecuta en un TomEE real.

El método @Deployment crea sobre la marcha la aplicación web a desplegar con las clases de este ejemplo. También puede agregar su aplicación REST y un web.xml. testable = false ejecuta los métodos de prueba como cliente de la aplicación desplegada, y @ArquillianResource inyecta su URL base.

Finalmente para probarlo usamos la API del cliente cxf para llamar al servicio REST en métodos get() y post(). Las cabeceras Accept y Content-Type seleccionan JSON o XML.

El contenedor se configura en src/test/resources/arquillian.xml: httpPort con valor -1 inicia TomEE en un puerto aleatorio.

package org.superbiz.rest;

import org.apache.cxf.jaxrs.client.WebClient;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.junit.runner.RunWith;

import jakarta.ws.rs.core.MediaType;
import java.io.IOException;
import java.net.URL;

import static org.junit.Assert.assertEquals;

@RunWith(Arquillian.class)
public class GreetingServiceTest {

    @ArquillianResource
    private URL base;

    @Deployment(testable = false)
    public static WebArchive app() {
        return ShrinkWrap.create(WebArchive.class).addClasses(GreetingService.class, Request.class, Response.class);
    }

    @Test
    public void getXml() throws IOException {
        final String message = WebClient.create(base.toExternalForm()).path("greeting/")
                .accept(MediaType.APPLICATION_XML_TYPE)
                .get(String.class);
        assertEquals("<response><value>Hi REST!</value></response>", message.replaceAll("<\\?[^>]*\\?>", "").trim());
    }

    @Test
    public void postXml() throws IOException {
        final String message = WebClient.create(base.toExternalForm())
                .path("greeting/")
                .type(MediaType.APPLICATION_XML_TYPE)
                .accept(MediaType.APPLICATION_XML_TYPE)
                .post("<request><value>Hi REST!</value></request>", String.class);
        assertEquals("<response><value>hi rest!</value></response>", message.replaceAll("<\\?[^>]*\\?>", "").trim());
    }

    @Test
    public void getJson() throws IOException {
        final String message = WebClient.create(base.toExternalForm())
                .path("greeting/")
                .accept(MediaType.APPLICATION_JSON_TYPE)
                .get(String.class);
        assertEquals("{\"value\":\"Hi REST!\"}", message);
    }

    @Test
    public void postJson() throws IOException {
        final String message = WebClient.create(base.toExternalForm())
                .path("greeting/")
                .type(MediaType.APPLICATION_JSON_TYPE)
                .accept(MediaType.APPLICATION_JSON_TYPE)
                .post(new Request("Hi REST!"), String.class);
        assertEquals("{\"value\":\"hi rest!\"}", message);
    }
}

#Running

Ejecutar el ejemplo es bastante simple. En el directorio rest-xml-json run:

$ mvn clean install

Lo que debería crear una salida como la siguiente.

Running org.superbiz.rest.GreetingServiceTest
INFO: Starting TomEE from: /tmp/arquillian-apache-tomee-21989
INFO: Starting Servlet engine: [Apache Tomcat/11.0.25]
INFO: Starting ProtocolHandler ["http-nio-52850"]
INFO: Configuring enterprise application: /path/to/tomee/examples/rest-xml-json/target/tomee-embedded/0/2f767e7a-173c-40c2-b7e1-78e2e1570f59
INFO: Enterprise application "/path/to/tomee/examples/rest-xml-json/target/tomee-embedded/0/2f767e7a-173c-40c2-b7e1-78e2e1570f59" loaded.
INFO: Deployed Application(path=/path/to/tomee/examples/rest-xml-json/target/tomee-embedded/0/2f767e7a-173c-40c2-b7e1-78e2e1570f59)
INFO: REST Application: http://localhost:52850/2f767e7a-173c-40c2-b7e1-78e2e1570f59/         -> org.apache.openejb.server.rest.InternalApplication
INFO:      Service URI: http://localhost:52850/2f767e7a-173c-40c2-b7e1-78e2e1570f59/greeting -> Pojo org.superbiz.rest.GreetingService
INFO:               GET http://localhost:52850/2f767e7a-173c-40c2-b7e1-78e2e1570f59/greeting ->      Response message()
INFO:              POST http://localhost:52850/2f767e7a-173c-40c2-b7e1-78e2e1570f59/greeting ->      Response lowerCase(Request)
INFO: Undeploying app: /path/to/tomee/examples/rest-xml-json/target/tomee-embedded/0/2f767e7a-173c-40c2-b7e1-78e2e1570f59
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.926 s -- in org.superbiz.rest.GreetingServiceTest
Results:

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0