Tutorial Series

Java EE 7 and JAX-RS 2.0

Adam Bien
java-ee-7-cueball

Adam Bien introduces us to one of the key features of Java EE 7, used for leaner Java RESTful applications.

Java EE 7 with JAX-RS 2.0 brings several useful features,
which further simplify development and lead to the creation of even
more-sophisticated, but lean, Java SE/EE RESTful applications. Adam
Bien introduces us to one of the key features of Java EE
7. Reprinted with permission from the Oracle Technology
Network, Oracle Corporation.


Sample Code

Most Java EE 6 applications, with the requirement for a remote
API and free choice, are using a more or less RESTful flavor of the
JAX-RS 1.0 specification. Java EE 7 with JAX-RS 2.0 brings several
useful features, which further simplify development and lead to the
creation of even more-sophisticated, but lean, Java SE/EE RESTful
applications.

Roast House

Roast House is a Java-friendly but simplistic JAX-RS 2.0
example, which manages and roasts some coffee beans. The roast
house itself is represented as
CoffeeBeansResource. The
URI "coffeebeans" uniquely identifies
the CoffeeBeansResource (see Listing
1
).

Listing 1

//...
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.container.ResourceContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
@ApplicationScoped
@Path("coffeebeans")
public class CoffeeBeansResource {
    
    @Context
    ResourceContext rc;
    
    Map<String, Bean> bc;

    @PostConstruct
    public void init() {
        this.bc = new ConcurrentHashMap<>();
    }

    @GET
    public Collection<Bean> allBeans() {
        return bc.values();
    }

    @GET
    @Path("{id}")
    public Bean bean(@PathParam("id") String id) {
        return bc.get(id);
    }

    @POST
    public Response add(Bean bean) {
        if (bean != null) {
            bc.put(bean.getName(), bean);
        }
        final URI id = URI.create(bean.getName());
        return Response.created(id).build();
    }

    @DELETE
    @Path("{id}")
    public void remove(@PathParam("id") String id) {
        bc.remove(id);
    }
    
    @Path("/roaster/{id}")
    public RoasterResource roaster(){
        return this.rc.initResource(new RoasterResource());
    }
}

 

As in the previous JAX-RS specifications, a resource can be
@Singleton or @Stateless EJB.
In addition, all root resources, providers,
and Application subclasses can be deployed
as managed or CDI-managed beans. Injection is also available in all
extensions annotated with
the @Provider annotation, which simplifies
integration with existing code. JAX-RS–specific components can be
also injected into sub-resources using
the ResourceContext:

Listing 2

    @Context
    ResourceContext rc;

    @Path("/roaster/{id}")
    public RoasterResource roaster(){
        return this.rc.initResource(new RoasterResource());
    }

 

Interestingly
the javax.ws.rs.container.ResourceContext not
only allows you to inject JAX-RS information into an existing
instance, but also provides you access to the resource classes with
the ResourceContext#getResource(Class<T>
resourceClass)
 method. Injection points of instances
passed to
the ResourceContext#initResource method are
set with values from the current context by the JAX-RS runtime. The
field String id in
the RoasterResource class (shown in
Listing 3) receives the value of the path
parameter of the parent’s resource:

Listing 3

public class RoasterResource {

    @PathParam("id")
    private String id;

    @POST
    public void roast(@Suspended AsyncResponse ar, Bean bean) {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
        }
        bean.setType(RoastType.DARK);
        bean.setName(id);
        bean.setBlend(bean.getBlend() + ": The dark side of the bean");
        Response response = Response.ok(bean).header("x-roast-id", id).build();
        ar.resume(response);
    }
}

 

The
parameter javax.ws.rs.container.AsyncResponse is
similar to the Servlet
3.0 javax.servlet.AsyncContext class and
allows asynchronous request execution. In the above example, the
request is suspended for the processing duration and the response
is pushed to the client with the invocation of the
method AsyncResponse#resume. The
method roast is still executed
synchronously, so the asynchronous execution does not bring any
asynchronous behavior at all. However, the combination of
EJB’s @javax.ejb.Asynchronous annotation and
the @Suspended AsyncResponse enables
asynchronous execution of business logic with eventual notification
of the interested client. Any JAX-RS root resource can be annotated
with @Stateless or @Singletonannotations
and can, in effect, function as an EJB (see Listing
4
):

Listing 4

import javax.ejb.Asynchronous;
import javax.ejb.Singleton;

@Stateless
@Path("roaster")
public class RoasterResource {

    @POST
    @Asynchronous
    public void roast(@Suspended AsyncResponse ar, Bean bean) {
    //heavy lifting
        Response response = Response.ok(bean).build();
        ar.resume(response);
    }
}

 

An @Asynchronous resource method with
an @Suspended AsyncResponse parameter is
executed in a fire-and-forget fashion. Although the
request-processing thread is freed immediately,
the AsyncResponse still provides a
convenient handle to the client. After the completion of
time-consuming work, the result can be conveniently pushed back to
the client. Usually, you would like to separate JAX-RS– specific
behavior from the actual business logic. All business logic could
be easily extracted into a dedicated boundary EJB, but CDI eventing
is even better suited for covering the fire-and-forget cases. The
custom event class RoastRequest carries the
payload (Bean class) as processing input and
the AsyncResponse for the resulting
submission (see Listing 5):

Listing 5

public class RoastRequest {

    private Bean bean;
    private AsyncResponse ar;

    public RoastRequest(Bean bean, AsyncResponse ar) {
        this.bean = bean;
        this.ar = ar;
    }

    public Bean getBean() {
        return bean;
    }

    public void sendMessage(String result) {
        Response response = Response.ok(result).build();
        ar.resume(response);
    }

    public void errorHappened(Exception ex) {
        ar.resume(ex);
    }
}

 

CDI events not only decouple the business logic from the JAX-RS
API, but also greatly simplify the JAX-RS code (see Listing
6)
:

Listing 6

public class RoasterResource {

    @Inject
    Event<RoastRequest> roastListeners;

    @POST
    public void roast(@Suspended AsyncResponse ar, Bean bean) {
        roastListeners.fire(new RoastRequest(bean, ar));
    }
}

 

Any CDI managed bean or EJB could receive
the RoastRequest in a publish-subscribe
style and synchronously or asynchronously process the payload with
a simple observer method: void onRoastRequest(@Observes
RoastRequest request){}
.

With the AsyncResponse class the JAX-RS
specification introduces an easy way to push information to HTTP in
real time. From the client perspective, the asynchronous request on
the server is still blocking and so synchronous. From the
REST-design perspective, all long-running tasks should return
immediately with the HTTP status code 202 along with additional
information about how to get the result after the processing
completes.

The Return of Aspects

Popular REST APIs often require their clients to compute a
fingerprint of the message and send it along with the request. On
the server side, the fingerprint is computed and compared with the
attached information. If both don’t match, the message gets
rejected. With the advent of JAX-RS and the introduction
of javax.ws.rs.ext.ReaderInterceptor
javax.ws.rs.ext.WriterInterceptor
, the traffic can be
intercepted on the server side and even on the client side. An
implementation of
the ReaderInterceptor interface on the
server wraps
the MessageBodyReader#readFrom and is
executed before the actual serialization.

The PayloadVerifier fetches the signature
from the header, computes the fingerprint from the stream, and
eventually invokes
theReaderInterceptorContext#proceed method, which
invokes the next interceptor in the chain or
the MessageBodyReader instance (see
Listing 7).

Listing 7

public class PayloadVerifier implements ReaderInterceptor{

    public static final String SIGNATURE_HEADER = "x-signature";

    @Override
    public Object aroundReadFrom(ReaderInterceptorContext ric) throws IOException, 
WebApplicationException {
        MultivaluedMap<String, String> headers = ric.getHeaders();
        String headerSignagure = headers.getFirst(SIGNATURE_HEADER);
        InputStream inputStream = ric.getInputStream();
        byte[] content = fetchBytes(inputStream);
        String payload = computeFingerprint(content);
        if (!payload.equals(headerSignagure)) {
            Response response = Response.status(Response.Status.BAD_REQUEST).header(
            SIGNATURE_HEADER, "Modified content").build();
            throw new WebApplicationException(response);
        }
        ByteArrayInputStream buffer = new ByteArrayInputStream(content);
        ric.setInputStream(buffer);
        return ric.proceed();
    }
    //...    
}

 

Modified content results in different fingerprints and causes
the raising of
the WebApplicationException with the
BAD_REQUEST (400) response code.

All the computation for fingerprints or for outgoing requests
can be easily automated with an implementation of
theWriterInterceptor. An implementation of
the WriterInterceptor wraps MessageBodyWriter#writeTo and
is executed before the serialization of the entity into a stream.
For the fingerprint computation, the final representation of the
entity “on-the-wire” is needed, so we pass
ByteArrayOutputStream as a buffer, invoke
the WriterInterceptorContext#proceed() method,
fetch the raw content and compute the fingerprint. See
Listing 8.

Listing 8

public class PayloadVerifier implements WriterInterceptor {
    public static final String SIGNATURE_HEADER = "x-signature";

   @Override
    public void aroundWriteTo(WriterInterceptorContext wic) throws IOException, 
WebApplicationException {
        OutputStream oos = wic.getOutputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        wic.setOutputStream(baos);
        wic.proceed();
        baos.flush();
        byte[] content = baos.toByteArray();
        MultivaluedMap<String, Object> headers = wic.getHeaders();
        headers.add(SIGNATURE_HEADER, computeFingerprint(content));
        oos.write(content);

    }
    //...
}

 

Finally, the computed signature is added as a header to the
request, the buffer is written to the original stream, and the
whole request is sent to the client. Of course, a single class can
also implement both interfaces at the same time:

Listing 9

import javax.ws.rs.ext.Provider;
@Provider
public class PayloadVerifier implements ReaderInterceptor, WriterInterceptor {
}

 

As in the previous JAX-RS releases, custom extensions are going
to be automatically discovered and registered with
the @Providerannotation. For the interception
of MessageBodyWriter and MessageBodyReader instances,
only the implementations of
theReaderInterceptor and WriterInterceptor have
to be annotated with
the @Provider annotation—no additional
configuration or API calls are required.

Request Interception

An implementation of
ContainerRequestFilter and ContainerResponseFilter intercepts
the entire request, not only the process of reading and writing of
entities. The functionality of both interceptors is far more useful
than logging of the information contained in
raw javax.servlet.http.HttpServletRequest instance.
The class TrafficLogger is not only able to
log the information contained in
the HttpServletRequest, but also to trace the
information about the resources matching a particular request, as
shown in Listing 10.

Listing 10

@Provider
public class TrafficLogger implements ContainerRequestFilter, ContainerResponseFilter {

    //ContainerRequestFilter
    public void filter(ContainerRequestContext requestContext) throws IOException {
        log(requestContext);
    }
    //ContainerResponseFilter
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext 
                                                 responseContext) throws IOException {
        log(responseContext);
    }

    void log(ContainerRequestContext requestContext) {
        SecurityContext securityContext = requestContext.getSecurityContext();
        String authentication = securityContext.getAuthenticationScheme();
        Principal userPrincipal = securityContext.getUserPrincipal();
        UriInfo uriInfo = requestContext.getUriInfo();
        String method = requestContext.getMethod();
        List<Object> matchedResources = uriInfo.getMatchedResources();
        //...
    }

    void log(ContainerResponseContext responseContext) {
        MultivaluedMap<String, String> stringHeaders = responseContext.getStringHeaders();
        Object entity = responseContext.getEntity();
    //...
    }
}

 

Accordingly, a registered implementation of
the ContainerResponseFilter gets an instance
of the ContainerResponseContextand is able to
access the data generated by the server. Status codes and header
contents, for example, the Location header,
are easily
accessible. ContainerRequestContext as well
as ContainerResponseContext are mutable
classes which can be modified by the filters.

Without any additional configuration
the ContainerRequestFilter is executed after
the HTTP-resource matching phase. At this point in time it is no
longer possible to modify the incoming request in order to
customize the resource binding. In case you would like to influence
the binding between the request and a resource,
ContainerRequestFilter can be configured
to be executed before the resource binding phase.
Any ContainerRequestFilter annotated with
the javax.ws.rs.container.PreMatching annotation
is executed before the resource binding, so the HTTP request
contents can be tweaked for the desired mapping. A common use case
for the @PreMatching filters is adjusting
the HTTP verbs to overcome limits in the networking infrastructure.
More “esoteric” methods
likePUTOPTIONSHEAD,
or DELETE may be filtered out by firewalls
or not supported by some HTTP clients. @PreMatching
ContainerRequestFilter
 implementation could fetch the
information from the header (for example,
X-HTTP-Method-Override“) indicating the desired HTTP
verb and could change a POST request into
PUT on the fly (see Listing
11
).

Listing 11

@Provider
@PreMatching
public class HttpMethodOverrideEnabler implements ContainerRequestFilter {

    public void filter(ContainerRequestContext requestContext) throws IOException {
        String override = requestContext.getHeaders()
                .getFirst("X-HTTP-Method-Override");
        if (override != null) {
            requestContext.setMethod(override);
        }
    }
}

Configuration

All interceptors and filters registered with
the @Provider annotation are globally
enabled for all resources. At deployment time the server scans the
deployment units for @Provider annotations
and automatically registers all extensions before the activation of
the application. All extensions can be packaged into dedicated JARs
and deployed on demand with the WAR (in
the WEB-INF/lib folder). The JAX-RS runtime
would scan the JARs and automatically register the extensions.
Drop-in deployment of self-contained JARs is nice, but requires
fine-grained extension packaging. All extensions contained within a
JAR would be activated at once.

JAX-RS introduces binding annotations for selective decoration
of resources. The mechanics are similar to CDI qualifiers. Any
custom annotation denoted with the
meta-annotation javax.ws.rs.NameBinding can
be used for the declaration of interception points:

Listing 12

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Tracked {
}

 

All interceptors or filters denoted with
the Tracked annotation can be selectively
activated by applying the
same Tracked annotation on classes, methods,
or even subclasses of the Application:

Listing 13

@Tracked
@Provider
public class TrafficLogger implements ContainerRequestFilter, ContainerResponseFilter {
}

 

Custom NameBinding annotations can be
packaged together with the corresponding filter or interceptor and
selectively applied to resources by the application developer.
Although the annotation-driven approach significantly increases
flexibility and allows coarser plug-in packages, the binding is
still static. The application needs to be recompiled and
effectively redeployed to change the interceptor or filter
chain.

In addition to the global and annotation-driven configuration of
cross-cutting functionality, JAX-RS 2.0 also introduces a new API
for dynamic extension registration. An implementation of
the javax.ws.rs.container.DynamicFeature interface
annotated with the@Provider annotation is used by
the container as a hook for the registration of interceptors and
filters dynamically, without the need for recompilation.
The LoggerRegistration extension
conditionally registers
the PayloadVerifier interceptor
and TrafficLoggerfilter by querying the existence
of predefined system properties, as shown in Listing
14
:

Listing 14

@Provider
public class LoggerRegistration implements DynamicFeature {

    @Override
    public void configure(ResourceInfo resourceInfo, FeatureContext context) {
        String debug = System.getProperty("jax-rs.traffic");
        if (debug != null) {
            context.register(new TrafficLogger());
        }
        String verification = System.getProperty("jax-rs.verification");
        if (verification != null) {
            context.register(new PayloadVerifier());
        }
    }
}  

The Client Side

The JAX-RS 1.1 specification did not cover the client. Although
proprietary implementations of a client REST API, such as RESTEasy
or Jersey, could communicate with any HTTP resource (not even
implemented with Java EE), the client code was directly dependent
on the particular implementation. JAX-RS 2.0 introduces a new,
standardized Client API. Using a standardized bootstrapping, the
Service Provider Interface (SPI) is replaceable. The API is fluent
and similar to the majority of the proprietary REST client
implementations (see Listing 15).

Listing 15

import java.util.Collection;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

public class CoffeeBeansResourceTest {

    Client client;
    WebTarget root;

    @Before
    public void initClient() {
        this.client = ClientBuilder.newClient().register(PayloadVerifier.class);
        this.root = this.client.target("http://localhost:8080/roast-house/api/coffeebeans");
    }

    @Test
    public void crud() {
        Bean origin = new Bean("arabica", RoastType.DARK, "mexico");
        final String mediaType = MediaType.APPLICATION_XML;
        final Entity<Bean> entity = Entity.entity(origin, mediaType);
        Response response = this.root.request().post(entity, Response.class);
        assertThat(response.getStatus(), is(201));

        Bean result = this.root.path(origin.getName()).request(mediaType).get(Bean.class);
        assertThat(result, is(origin));
        Collection<Bean> allBeans = this.root.request().get(
new GenericType<Collection<Bean>>() {
        });
        assertThat(allBeans.size(), is(1));
        assertThat(allBeans, hasItem(origin));

        response = this.root.path(origin.getName()).request(mediaType).delete(Response.class);
        assertThat(response.getStatus(), is(204));

        response = this.root.path(origin.getName()).request(mediaType).get(Response.class);
        assertThat(response.getStatus(), is(204));
    }
//..
}

 

In the integration test above, the
default Client instance is obtained using
the
parameterless ClientFactory.newClient() method.
The bootstrapping process itself is standardized with the
internal javax.ws.rs.ext.RuntimeDelegate abstract
factory. Either an existing instance
of RuntimeDelegate is injected (by, for
example, a Dependency Injection framework) into
the ClientFactory or it is obtained by
looking for a hint in the
files META-INF/services/javax.ws.rs.ext.RuntimeDelegate and${java.home}/lib/jaxrs.properties and
eventually by searching for
the javax.ws.rs.ext.RuntimeDelegate system
property. By a failed discovery, a default (Jersey) implementation
is attempted to initialize.

The main purpose of
javax.ws.rs.client.Client is the enabling
of fluent access
to javax.ws.rs.client.WebTarget orjavax.ws.rs.client.Invocation instances.
WebTarget represents a JAX-RS resource and
an Invocation is a ready-to-use request
waiting for submission. WebTarget is also
an Invocation factory.

In the
method CoffeBeansResourceTest#crud() the Bean object
is passed back and forth between client and server. With the choice
of MediaType.APPLICATION_XML, only a few JAXB
annotations are needed to send and receive a DTO serialized in an
XML document:

Listing 16

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Bean {

    private String name;
    private RoastType type;
    private String blend;

}

 

The names of the class and attributes have to match for
successful marshaling with the server’s representation, but the DTO
does not have to be binary compatible. In the above example,
both Bean classes are located in different
packages and even implement different methods. A
desired MediaType is passed to
the WebTarget#request() method, which
returns an instance of a
synchronousInvocation.Builder. The final invocation of
a method named after the HTTP verbs
(GETPOSTPUTDELETEHEADOPTIONS,
orTRACE) initiates a synchronous request.

The new client API also supports asynchronous resource
invocation. As mentioned earlier,
an Invocation instance decouples the request
from the submission. An asynchronous request can be initiated with
the chained async() method invocation, which
returns anAsyncInvoker instance. See
Listing 17.

Listing 17

    @Test
    public void roasterFuture() throws Exception {
    //...
        Future<Response> future = this.root.path("roaster").path("roast-id").request().async().post(entity);
        Response response = future.get(5000, TimeUnit.SECONDS);
        Object result = response.getEntity();
        assertNotNull(result);
        assertThat(roasted.getBlend(),containsString("The dark side of the bean"));
    }

 

There is not a lot of benefit in the “quasi-asynchronous”
communication style in the above example—the client still has to
block and wait until the response arrives. However,
the Future-based invocation is very useful for
batch-processing: the client can issue several requests at once,
gather the Future instances, and process
them later.

A truly asynchronous implementation can be achieved with a
callback registration, as shown in Listing 18:

Listing 18

    @Test
    public void roasterAsync() throws InterruptedException {
    //...
        final Entity<Bean> entity = Entity.entity(origin, mediaType);
        this.root.path("roaster").path("roast-id").request().async().post(
entity, new InvocationCallback<Bean>() {
            public void completed(Bean rspns) {
            }

            public void failed(Throwable thrwbl) {
            }
        });
    }

 

For each method returning a Future, there is
also a corresponding callback method available. An implementation
of theInvocationCallback interface is accepted as
the last parameter of the method (post(), in the
example above) and is asynchronously notified upon successful
invocation with the payload or—in a failure case—with an
exception.

An automated construction of URIs can be streamlined with the
built-in templating mechanism. Predefined placeholders can be
replaced shortly before the request execution and save repetitive
creation of WebTarget instances:

Listing 19

    @Test
    public void templating() throws Exception {
        String rootPath = this.root.getUri().getPath();
        URI uri = this.root.path("{0}/{last}").
                resolveTemplate("0", "hello").
                resolveTemplate("last", "REST").
                getUri();
        assertThat(uri.getPath(), is(rootPath + "/hello/REST"));
    }

 

A small, but important detail: on the client side, extensions
are not discovered at initialization time; rather, they have to be
explicitly registered with the Client
instance: ClientFactory.newClient().register(PayloadVerifier.class).
However, the same entity interceptor implementations can be shared
between client and server, which simplifies testing, reduces
potential bugs, and increases productivity. The already
introduced PayloadVerifier interceptor can
be reused without any change on the client side as well.

Conclusion: Java EE or Not?

Interestingly, JAX-RS does not even require a full-fledged
application server. After fulfilling the specified Context Types, a
JAX-RS 2.0–compliant API can be anything. However, the combination
with EJB 3.2 brings asynchronous processing, pooling (and so
throttling), and monitoring. Tight integration with Servlet 3+
comes with efficient asynchronous processing
of @Suspended responses
throughAsyncContext support and CDI runtime
brings eventing. Also Bean Validation is well integrated and can be
used for the validation of resource parameters. Using JAX-RS 2.0
together with other Java EE 7 APIs brings the most convenient (=no
configuration) and most productive (=no re-invention) way of
exposing objects to remote systems.

See Also

Author Bio: Consultant and author Adam
Bien is an Expert Group member for the Java EE 6/7, EJB 3.X,
JAX-RS, and JPA 2.X JSRs. He has worked with Java technology since
JDK 1.0 and with servlets/EJB 1.0 and is now an architect and
developer for Java SE and Java EE projects. He has edited several
books about JavaFX, J2EE, and Java EE, and he is the author of
Real World Java EE
Patterns—Rethinking Best Practices
 and Real
World Java EE Night Hacks
. Adam is also a Java Champion,
Top Java Ambassador 2012, and JavaOne 2009, 2011, and 2012 Rock
Star. Adam organizes occasional Java (EE) workshops at Munich’s Airport.

Reprinted with permission from the Oracle Technology
Network, Oracle Corporation

Author
Adam Bien
Consultant and author Adam Bien is an Expert Group member for the Java EE 6/7, EJB 3.X, JAX-RS, and JPA 2.X JSRs. He has worked with Java technology since JDK 1.0 and with servlets/EJB 1.0 and is now an architect and developer for Java SE and Java EE projects. He has edited several books about JavaFX, J2EE, and Java EE, and he is the author of Real World Java EE Patterns
Comments
comments powered by Disqus