Servlet mapping

In order to enable JAX-RS in a Java EE application the developer must set up the configuration in the web deployment descriptor file. JAX-RS requires a specific servlet mapping to be enabled, which triggers the provider to search for annotated classes.

The standard API for JAX-RS lies in the Java package javax.ws.rs and in its subpackages. Interestingly, the Java API for REST style interfaces sits underneath the Java Web Services package javax.ws.

For your web applications, you must configure a Java servlet with just fully qualified name of the class javax.ws.rs.core.Application. The Servlet must be mapped to a root URL pattern in order to intercept REST style requests.

The following is an example code of such a configuration:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
  http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
  version="3.1"
  metadata-complete="false">
  <display-name>JavaEE Handbook 7 JAX RS Basic
   </display-name>
  <servlet>
    <servlet-name>javax.ws.rs.core.Application
    </servlet-name>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>javax.ws.rs.core.Application
    </servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

In the web deployment descriptor we just saw, only the servlet name is defined, javax.ws.rs.core.Application. Do not define the servlet class. The second step maps the URL pattern to the REST endpoint path. In this example, any URL that matches the simplified Glob pattern /rest/* is mapped. This is known as the application path.

A JAX-RS application is normally packaged up as a WAR file and deployed to a Java EE application server or a servlet container that conforms to the Web Profile. The application classes are stored in the /WEB-INF/classes and required libraries are found under /WEB-INF/lib folder. Therefore, JAX-RS applications share the consistency of configurations as servlet applications.

The JAX-RS specification does recommend, but does not enforce, that conformant providers follow the general principles of Servlet 3.0 plug-ability and discoverability of REST style endpoints. Discoverability is achieved in the recommendation through class scanning of the WAR file, and it is up to the provider to make this feature available.

An application can create a subclass of the javax.ws.rs.core.Application class. The Application class is a concrete class and looks like the following code:

public class Application {
  public Application() { /* ... */ }
  public java.util.Set<Class<?>> getClasses() { 
    /* ... */ }
  public java.util.Set<Object> getSingletons() { 
    /* ... */ }
  public java.util.Map<String,Object> getProperties() { 
    /* ... */  }
}

Implementing a custom Application subclass is a special case of providing maximum control of RESTful services for your business application. The developer must provide a set collection of classes that represent JAX-RS end points. The engineer must supply a list of Singleton object, if any, and do something useful with the properties.

The default implementation of javax.ws.rs.core.Application and the methods getClasses() and getSingletons() return empty sets. The getProperties() method returns an empty map collection. By returning empty sets, the provider assumes that all relevant JAX-RS resource and provider classes that are scanned and found will be added to the JAX-RS application.

Majority of the time, I suspect, you, the developer will want to rely on annotations to specify the REST end points and there are Servlet Context listeners and Servlet Filters to configure application wide behavior, for example the startup sequence ofa web application.

So how can we register our own custom Application subclass? The answer is just subclass the core class with your own class.

The following code explains what we just read:

package com.fizbuzz.services;

@javax.ws.rs.ApplicationPath("rest")
public class GreatApp extends javax.ws.rs.core.Application {
  // Do your custom thing here
}

In the custom GreatApp class, you can now configure logic for initialization. Note the use of @ApplicationPath annotation to configure the REST style URL. You still have to associate your custom Application subclass into the web deployment descriptor with a XML Servlet name definition.

Tip

Remember the Application Configuration

A very common error for first time JAX-RS developers is to forget that the web deployment descriptor really requires a servlet mapping to a javax.ws.js.core.Application type.

Now that we know how to initialize a JAX-RS application, let us dig deeper and look at the defining REST style endpoints.

Mapping JAX-RS resources

JAX-RS resources are configured through the resource path, which suffixes the application path. Here is the constitution of the URL.

http://<hostname>:<port>/<web_context>/<application_path>/<resource_path>

The <hostname> is the host name of the server. The <port> refers to the port number, which is optional and the default port is 80. The <web_context> is the Servlet context, which is the context for the deployed web application. The <application_path> is the configuration URI pattern as specified in the web deployment descriptor @ApplicationPath or the Servlet configuration of Application type. The <resource_path> is the resource path to REST style resource.

The final fragment <resource_path> defines the URL pattern that maps a REST style resource. The resource path is configured by annotation javax.ws.rs.Path.

Test-Driven Development with JAX-RS

Let us write a unit test to verify the simplest JAX-RS service. It follows a REST style resource around a list of books. There are only four books in this endpoint and the only thing the user/client can do at the start is to access the list of books by author and title. The client invokes the REST style endpoint, otherwise known as a resource with an HTTP GET request.

The following is the code for the class RestfulBookService:

package je7hb.jaxrs.basic;
import javax.annotation.*;
import javax.ws.rs.*;
import java.util.*;

@Path("/books")
public class RestfulBookService {
  private List<Book> products = Arrays.asList(new Book("Sir Arthur Dolan Coyle","Sherlock Holmes and the Hounds of the Baskervilles"),
    new Book("Dan Brown", "Da Vinci Code"),
    new Book("Charles Dickens", "Great Expectations"),
    new Book("Robert Louis Stevenson", "Treasure Island"));

  @GET
  @Produces("text/plain")
  public String getList() {
    StringBuffer buf = new StringBuffer();
    for (Book b: products) { 
      buf.append(b.title); buf.append('
'), }
    return buf.toString();
  }

@PostConstruct
public void acquireResource() { /* ... */ }
@PreDestroy
public void releaseResource() { /* ... */ }

static class Book {
  public final String author;
  public final String title;
  Book(String author, String title) {
    this.author = author;
    this.title = title;
    }
  }
}

The annotation @javax.ws.rs.Path declares the class as a REST style end-point for a resource. The @Path annotation is assigned to the class itself. The path argument defines the relative URL pattern for this resource, namely/books.

The method getList() is the interesting one. It is annotated with both @javax.ws.rs.GET and @javax.ws.rs.Produces.

The @GET is one of the six annotations that conform to the HTTP web request methods. It indicates that the method is associated with HTTP GET protocol request.

The @Produces annotation indicates the MIME content that this resource will generate. In this example, the MIME content is text/plain.

The other methods on the resource bring CDI into the picture. In the example, we are injecting post construction and pre destruction methods into the bean.

This is the only class that we require for a simple REST style application from the server side. With an invoking of the web resource with a HTTP GET Request like http://localhost:8080/mywebapp/rest/books we should get a plain text output with list of titles like the following:

Sherlock Holmes and the Hounds of the Baskervilles
Da Vinci Code
Great Expectations
Treasure Island

So do we test this REST style interface then? We could use Arquillian Framework directly, but this means our tests have to be built specifically in a project and it complicates the build process. Arquillian uses another open source project in the JBoss repository called ShrinkWrap . The framework allows the construction of various types of virtual Java archives in a programmatic fashion. We use ShrinkWrap just to build a Web Archive, and the code is really quite straightforward, because we have already seen it in previous chapters.

Let's look at the unit test class RestfulBookServiceTest in the following code:

package je7hb.jaxrs.basic;
// import omitted

public class RestfulBookServiceTest {
  @Test
  public void shouldAssembleAndRetrieveBookList() 
  throws Exception {
    WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war")
      .addClasses(RestfulBookService.class)
      .setWebXML(new File("src/main/webapp/WEB-INF/web.xml"))
      .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml");
    File warFile = new File(webArchive.getName());
    new ZipExporterImpl(webArchive)
      .exportTo(warFile, true);
    SimpleEmbeddedRunner runner =SimpleEmbeddedRunner.launchDeployWarFile(warFile, "mywebapp", 8080);
    try {
      URL url = new URL("http://localhost:8080/mywebapp/rest/books");
      InputStream inputStream = url.openStream();
      BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
      List<String> lines = new ArrayList<>();
      String text = null;
      int count=0;
      while ( ( text = reader.readLine()) != null ) {
        lines.add(text);
        ++count;
        System.out.printf("**** OUTPUT **** text[%d] = %s
", count, text );
      }
        assertFalse( lines.isEmpty() );
        assertEquals("Sherlock Holmes and the Hounds of the Baskervilles", lines.get(0));
        assertEquals("Da Vinci Code", lines.get(1));
        assertEquals("Great Expectations", lines.get(2));
        assertEquals( "Treasure Island", lines.get(3) );
    }
    finally {
      runner.stop();
    }
  }
}

In the unit test method, shouldAssembleAndRetrieveBookList(), we first assemble a virtual web archive with an explicit name test.war. The WAR file contains the RestfulBookService services, the Web deployment descriptor file web.xml and an empty beans.xml file, which if you remember is only there to trigger the CDI container into life for this web application.

With the virtual web archive, we export the WAR as a physical file with the utility ZipExporterImpl class from ShinkWrap library, which creates the file test.war in the project root folder.

Next, we fire up the SimpleEmbeddedRunner utility that is part of the book's appendix. It deploys the web archive to an embedded GlassFish container. Essentially, this is the boilerplate to get to deliver a test result.

We, then, get to the heart of the test itself; we construct a URL to invoke the REST style endpoint, which is http://localhost:8080/mywebapp/rest/books. We read the output from the service endpoint with Java standard I/O line by line into a list collection of Strings. Once we have a list of collections, then we assert each line against the expected output from the rest style service.

Because we acquired an expensive resource, like an embedded Glassfish container, we are careful to release it, which is the reason, why we surround critical code with a try-finally block statement. When the execution comes to end of test method, we ensure the embedded GlassFish container is shut down.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.39.144