Preloader image

Defining a REST service is pretty easy, simply ad @Path annotation to a class then define on methods the HTTP method to use (@GET, @POST, …).

The Code

The REST service: @Path, @Produces, @Consumes

Here we see a bean that uses the @Path, @Produces and @Consumes annotations. The @Path annotation identifies the URI path template to which the resource responds, and is specified at the class level of a resource. The @Path annotation’s value is a partial URI path template relative to the base URI of the server on which the resource is deployed, the context root of the WAR, and the URL pattern to which the Jersey helper servlet responds. The @Produces annotation is used to specify the MIME media types or representations a resource can produce and send back to the client. If @Produces is applied at the class level, all the methods in a resource can produce the specified MIME types by default. If it is applied at the method level, it overrides any @Produces annotations applied at the class level. The @Consumes annotation is used to specify which MIME media types of representations a resource can accept, or consume, from the client. If @Consumes is applied at the class level, all the response methods accept the specified MIME types by default. If @Consumes is applied at the method level, it overrides any @Consumes annotations applied at the class level.

Actually lines:

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

are optional since it is the default configuration. And these lines can be configured by method too if you need to be more precise.

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

Testing

Test for the JAXRS service

The test uses Arquillian with the TomEE embedded adapter, so the REST service runs in a real TomEE.

The @Deployment method creates on the fly the web application to deploy with the classes of this example. You can add your REST Application and a web.xml too. testable = false runs the test methods as a client of the deployed application, and @ArquillianResource injects its base URL.

Finally to test it we use cxf client API to call the REST service in get() and post() methods. The Accept and Content-Type headers select JSON or XML.

The container is configured in src/test/resources/arquillian.xml: httpPort set to -1 starts TomEE on a random port.

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

Running the example is fairly simple. In the ``rest-xml-json'' directory run:

$ mvn clean install

Which should create output like the following.

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