A RESTful API requires implementing a server to process the HTTP requests. When REST was a new concept, the first tool at hand for creating REST servers in Java were servlets—and indeed current solutions are still based on servlets. But when the Java API for RESTful Web Services (JAX-RS) was released, it greatly simplified RESTful server implementation. JAX-RS is a specification for a Java API creating RESTful services, and is part of the Java Platform, Enterprise Edition (Java EE). Besides its interfaces it provides a number of annotations that allow you to declaratively mark classes and methods as endpoints for your RESTful API. The specification is available as the dependency javax.ws.rs-api.
Resource Interface
In JAX-RS you will create a class or an interface to act as the endpoint to a RESTful API request. This endpoint will define your RESTful service.
Using the examples from the lesson on REST, we might make a PensResource interface for providing remote access to managing the pens on a farm.
Binding URI Paths
You will need to indicate to JAX-RS what path your resource interface will be servicing. JAX-RS provides the javax.ws.rs.Path annotation to indicate the URI path(s) the implementation will service. The @Path annotation may contain template parameters to indicate that that part of the path will change based upon which resource is being accessed. Closely related is the javax.ws.rs.PathParam annotation which binds a method parameter to one of the URI template parameters specified by @Path.
Assuming that the JAX-RS application (more on this later) is mapped to the base path /farm/ in the root of the servlet container, the annotation @Path("pens") will map the resource to the /farm/pens URI path. Methods that have no further @Path designation such as getPens() will also be mapped to this base path. If a method has a further @Path designation, it will be considered relative to the resource path. For example the getAnimals(…) method will be mapped to /farm/pens/{penId}.
URI Path Templates
Path designations are templates and may contain replacement variables. Most common is simple placing the variable name between curly brackets { and }. The string /farm/pens/{penId} for example indicates that the {penId} part of the path, identified by the replacement variable penId, will depend on the value passed in the actual URI of the request.
The javax.ws.rs.PathParam annotation binds a method parameter to one of the URI template parameters specified by @Path. The designated variables in the path template are referred to using the @PathParam annotating a method parameter. JAX-RS will automatically extract the relevant replacement values for the indicated variables, and make them available as arguments when it calls the method! For example if a user access a URI with the path /farm/pens/pigpen, the ID pigpen would be passed to the getAnimals(…) method as the argument to the penId method parameter.
Indicating URI Query Parameters
As you experienced when documenting your RESTful API using the OpenAPI, some parameters that specify how values should be retrieved may not appear as variables in the URI path but as URI query parameters. JAX-RS provides the javax.ws.rs.QueryParam annotation for mapping URI query parameters to Java method parameters. In conjunction with the optional javax.ws.rs.DefaultValue annotation, @QueryParam names the query parameter that corresponds to the Java parameter variable. Java knows how to convert query parameter string values to Java primitive types if needed.
Specifying HTTP Methods
Now that you've indicated which paths are mapped to which methods and to the resource, you need to specify which methods respond to which HTTP methods. This is as easy as using annotations such as javax.ws.rs.GET. JAX-RS provides annotations corresponding to most of the methods you're familiar with are available, including @GET, @HEAD, @PUT, @DELETE, and @POST. In fact JAX-RS will implement support for the HTTP HEAD method by default; you only need to use @HEAD if you want the implementation to be more efficient.
To put the cow Bessie in the pigpen, for example, this interface would instruct JAX-RS to allow a client to issue a PUT to /farm/pens/pigpen/animals/bessie, sending a representation of Bessie in the content of the HTTP request. (See below.)
Content Negotiation
Producing Content
JAX-RS will automatically perform content negotiation with the HTTP client, and based upon the request Accept header will call the correct method based upon the media type specified in the javax.ws.rs.Produces annotation. If you are able to produce resource representations, you could create separate methods e.g. getPensText() and getPensJSON() and annotate them individually with @Produces("text/plain; charset=UTF-8") and @Produces("application/json; charset=UTF-8"), respectively. If you decide to handle both types based upon the Accept header, you could annotation a single method with @Produces({"text/plain; charset=UTF-8", "application/json; charset=UTF-8"}), as shown below. The first content type listed will be the default if the client does not send an Accept header.You can marshall (generate resource representations for transfer from one place to another) many media types automatically with a third-party library such as Jackson, as explained below.
If you want finer control over exactly what sort of response is returned, you can make your method signature return a javax.ws.rs.core.Response object. Working with a Response instance is facilitated through use of the javax.ws.rs.core.Response.ResponseBuilder class.
Consuming Content
Your RESTful API may not only produce content in response to queries, it may also consume content, in response to the HTTP PUT method for instance. The particular media type(s) consumed is indicated using the javax.ws.rs.Consumes annotation. Here as well you consume many media types automatically with a third-party library such as Jackson, as explained below.
Resource Services Implementation
The PenResource interface only defines the RESTful API for Java, much as your OpenAPI specification of your RESTful interface described it for developers. You will still need to implement your RESTful resource interface. In your implementation you simply need to indicate your resource interface you are implementing; you do not need to repeat its JAX-RS annotations.
Application
You also need to create a class to represent your set of RESTful services, extending the javax.ws.rs.core.Application class. You will annotate your application class with javax.ws.rs.ApplicationPath to indicate the base URI path for your RESTful API. The Application.getClasses() method will return a set of classes you want JAX-RS to instantiate as needed. This is how you register the various classes you've defined, and will include things like:
Resource class implementations.
Marshalling providers (discussed below)
Exception mappers (discussed below)
If you are using a JAX-RS aware servlet container, that's all you need to do! The container will discover your JAX-RS application class; instantiated; mount it at the indicated URI path inside the container; and register its service implementations and related classes. If your servlet container is not JAX-RS aware, your JAX-RS implementation (e.g. RESTEasy) may provide an initializer library to provide this functionality.
Security
TODO talk briefly about @RolesAllowed and indicate that configuration is container-specific
RESTEasy
The JAX-RS implementation you will be using is RESTEasy from JBoss. Although RESTEasy was made to run with the JBoss WildFly application server, RESTEasy also works as a standalone JAX-RS implementation inside any servlet container. If your servlet container is JAX-RS aware, no further configuration is needed other than your JAX-RS application and related classes. For non-JAX-RS aware servlet containers that nevertheless support the latest servlet specification, RESTEasy provides the resteasy-servlet-initializer library. Include it in your project, and your servlet container will find your JAX-RS application implementation automatically.
Content
Manual Content Processing
If you want to produce content manually, you can return an instance of javax.ws.rs.core.StreamingOutput from your method. You can also consume or produce content by adding one of the following types as a method signature or as a return type, respectively:
java.io.InputStream
java.io.Reader
byte[]
String
char[]
Custom Marshalling
Rather than dealing with low-level streams, it would be better to use specific provider strategy implementations for custom marshalling. JAX-RS provides the javax.ws.rs.ext.MessageBodyReader<T> and javax.ws.rs.ext.MessageBodyWriter<T> interfaces with many options for you to indicate in your implementation which media type(s) and Java type(s) you support for reading and writing. The implementation will be annotated with javax.ws.rs.ext.Provider. Besides the @Provider annotation, a MessageBodyReader implementation will be annotated with @Consumes(…), while a MessageBodyWriter implementation will be annotated with @Produces(…). If you implement one of these providers, you will need to register it with the JAX-RS application as you did with your resource implementation(s).
The following example shows how you could write a custom message body writer for returning a list of objects as string representations of the list items, each on a separate line. Once this class is registered with JAX-RS, any resource implementation returning a Set<E> with an indicated @Produces type of "text/plain" will use this MessageBodyWriter to return content.
Jackson
You have already used the Jackson library for processing JSON. RESTEasy provides a convenient way to plug JSON marshalling providers into your JAX-RS application using the resteasy-jackson2-provider library. Including this library in your project should automatically provide marshalling for application/json response content types that follow JavaBeans POJO conventions. For consuming HTTP requests in JSON, you can parse the JSON tree as you did in a previous lesson, or implement a Jackson mapper as explained in the Jackson documentation, provided in the Resources section. If you decide to parse a JSON data stream directly, it would be best to place that implementation in a separate provider for your type, as explained above.
Imagine for example that you have the following implementation of Pen. When an HTTP client retrieves the pens, Jackson will produce JSON output similar to that shown below.
Review
Gotchas
If you don't indicate a default charset in your @Produces declaration, there is no guarantee which charset the browser will assume.
Don't forget to add your resource implementation class to those returned by your application's getClasses() method, or JAX-RS will not know about your resource service and nothing will appear at its associated URI path.
In the Real World
TODO
Think About It
TODO
Self Evaluation
Which JAX-RS annotation would you use to inject values into your RESTful implementation?
Task
Create a Booker JAX-RS application matching your RESTful API documentation, serving information from a PublicationRepository on the backend with Guice injection.
Create RESTful resource interface(s) matching the Booker RESTful API you created, placing them in a ….booker.server.rest.resources package.
Implement your RESTful API using service(s) in a ….booker.server.rest.services package. Allow full CRUD operations using JSON as a marshalling format.
If you need to implement any providers, place them in a ….booker.server.rest.providers package.
Use dependency injection to provide resource services that delegate the appropriate PublicationRepository. For now wire up your in-memory FakePublicationRepository as the implementation, initialized with the contents of your SnapshotPublicationRepository.In order to use dependency injection, you'll want to return singleton instances in your REST application rather than implementation classes.
Test the RESTful API using Postman.
Deploy your RESTful implementation. Your server should now support: