JAX-RS: Understanding the Basics


What is New in JAX-RS 2.1

Reactive Client API

JAX-RS 2.1 introduces RxInvoker which can help with removing InvocationCallbacks from the asynchronous client code. 

CompletionStage

Default CompletionStageRxInvoker can be accessed via Invocation.rx().

RxJava

Custom RxInvokerProvider can be registered with the Client as a provider. CXF ships one three such custom providers, org.apache.cxf.jaxrs.rx.client.ObservableRxInvokerProvider (RxJava1),

org.apache.cxf.jaxrs.rx2.client.ObservableRxInvokerProvider (RxJava2) and org.apache.cxf.jaxrs.rx2.client.FlowableRxInvokerProvider (RxJava2).

Registering it with the Client allows for working with RxJava1 Observable or RxJava2 Observable or Flowable by doing Invocation.rx(Class<T> clazz), example,

Invocation.rx(org.apache.cxf.jaxrs.rx2.client.FlowableRxInvoker.class), etc.

Please see JAX-RS RxJava for more information.

RxJava2

RxJava2 can be used as a rx type when registered.  Please see JAX-RS RxJava for full information.

Project Reactor

Project Reactor can be used as a rx type when registered.  Please see JAX-RS Project Reactor Support for full information.

CompletableFuture as a method return value

In JAX-RS 2.1 one can return CompletableFuture (or CompletionStage) from a resource method without having to deal directly with JAX-RS AsyncResponse API. 

Please see JAX-RS RxJava for more information about returning RxJava Observable.

Server Sent Events

JAX-RS 2.1 provides a comprehensive support for SSE.

org.apache.cxf/cxf-rt-rs-sse/3.2.0 dependency will need to be added. CXF SSE implementation currently depends on Atmosphere.

SubResources as Classes

Sometimes subresource may need to have the request context information available to them. One valid and simple approach is to pass these contexts to them from the parent class which instantiates a subresource - but sometimes this approach does not work.

In JAX-RS 2.0 one can use ResourceContext to instantiate a subresource instance with the runtime taking care of injecting the contexts if needed. JAX-RS 2.1 introduces a shortcut where returning a subresource class from a subresource locator method, with the runtime istantiating the class and injecting the contexts if needed

CXF NIO Extension

Please see JAX-RS NIO for more information about this CXF 3.2.0 extension which is based on the early JAX-RS 2.1 API prototype.

What is New in JAX-RS 2.0

Filters

Server

ContainerRequestFilter and ContainerResponseFilter are new server-side request and response filters which can be used to customize various properties of a given request and response.

ContainerRequestFilter annotated with a PreMatching annotation will be run before the runtime has matched a request to a specific JAX-RS root resource and method. Prematching filters can be used to affect the matching process.

The request filters without the PreMatching annotation will run after the JAX-RS resource method has been selected.

ContainerRequestFilter can be used to block a request.

The filters can be bound to individual resource methods only with the help of custom NameBindings.

Multiple request and response filters can be executed in the specific order by using javax.annotation.Priority annotations. See Priorities for more information. Request filters are sorted in the ascending order, response filters - in the descending order.

Client

ClientRequestFilter and ClientResponseFilter are new client-side request and response filters which can be used to customize various properties of a given request and response.

ClientRequestFilter can be used to block a request.

Request filters are sorted in the ascending order, response filters - in the descending order. See Priorities for more information.

Interceptors

ReaderInterceptor and WriterInterceptor can be used in addition to filters or on its own to customize requests and responses on server and client sides.

Interceptors can be useful to customize the reading/writing process and block JAX-RS MessageBodyWriter or MessageBodyReader providers.

The interceptors used on the server side can be bound to individual resource methods only with the help of custom NameBindings.

All interceptors are sorted in the ascending order. See Priorities for more information.

Dynamic Features

Dynamic Feature is a server side feature that can be used to attach request and response filters as well as reader and writer interceptors to specific resource methods. It is an alternative approach to using the NameBindings and offer a finer-grained control over the binding process.

Exceptions

Dedicated exception classes representing various HTTP error or redirect conditions have been introduced, see the 'javax.ws.rs' Package Exceptions section.

For example, instead of throwing a "new WebApplicationException(404)" one is better to do "new NotFoundException()". The finer-grained exception hierarchy allows for a finer-grained support of exception mappers. It also opens a way to check WebApplicationException and all of its subclasses when catching the HTTP exceptions on the client side.

Note that on the client side, ProcessingException can be used to catch client-related exceptions while ResponseProcessingException can be used to narrow down the client side exceptions specifically related to processing the response message.

Suspended invocations

One of the best JAX-RS 2.0 features is the support for server-side asynchronous invocations. Please see the AsyncResponse documentation which provides a very good overview of this feature.

See also this test resource.

Typically, the resource method accepting AsyncResponse will either store it and start a new thread to finish the request, the method will return and the invocation will be suspended, then eventually another thread (either the one which initiated an internal job or some other thread) will resume the suspended call. Note in this case the invocation will be suspended indefinitely until it is resumed.

Another approach is to have AsyncResponse suspended for a limited period of time only and also register a TimeoutHandler. The latter will be invoked when the invocation is resumed by the container after the timeout has expired and the handler will either complete the invocation or suspend it again till it is ready to finish it.

CompletionCallback can be registered with AsyncResponse to receive the notifications when the async response has been sent back.

ConnectionCallback is supported starting from CXF 3.0.0-milestone2.

This feature can help developers write very sophisticated asynchronous applications.

Please also see the page about CXF Continuations API which JAX-RS 2.0 AsyncResponse implementation is based upon and
how to configure CXFServlet.

Parameter converters

ParamConverterProvider can be used to manage the conversion of custom Objects to String and vice versa on the server and client sides, when processing JAX-RS parameters representing URI parts or headers or form elements and when a default conversion mechanism does not work. For example, java.util.Date constructor accepting a String may have to be replaced a custom ParamConverter.

Bean parameters

BeanParam can be used to get JAX-RS parameters representing URI parts or headers or form elements and also contexts injected into a single bean container.

Note the CXF extension supporting the injection of all the parameters of specific JAX-RS type (example, QueryParam("") MyBean) is different, it only allows to get all the query parameters injected, but it also does not require that bean properties are annotated with QueryParam/etc annotations.

ResourceInfo

ResourceInfo is a new JAX-RS context which can be injected into filters and interceptors and checked which resource class and method are about to be invoked.

Injection into subresources

Subresources can get JAX-RS contexts injected directly into their fields with the help of ResourceContext.

When possible, having a parent resource injecting the contexts into a given subresource instance via a setter or constructor can offer a much simpler alternative.

Updates to the matching algorithm

JAX-RS 2.0 supports a proper resource method selection in cases where multiple root resource classes have the same Path value, for example:

@Path("/")
public class Root1 {
   @Path("/1")
   @GET 
   public Response get() {...}
}

@Path("/")
public class Root2 {
   @Path("/2")
   @GET 
   public Response get() {...}
}

In JAX-RS 1.1 a request with URI such as "/1" is not guaranteed to be matched and in CXF 2.7.x or earlier the use of CXF specific ResourceComparator is required to ensure Root1 and its get() method gets selected. In CXF 3.0.0 Root1 get() will always be correctly selected. Note ResourceComparator may still be of help in some cases even in CXF 3.0.0.

Link is a utility class for building HTTP links as HTTP Link headers or application data links.
UriInfo, UriBuilder, Response and ResponseBuilder classes have been enhanced to support Link.

Client API

JAX-RS 2.0 Client API has been completely implemented in CXF 3.0.0, please see the Client API page for more information.

Resource class

A resource class is a Java class annotated with JAX-RS annotations to represent a Web resource. Two types of resource classes are available: root resource classes and subresource classes. A root resource class is annotated with at least a @Path annotation, while subresource classes typically have no root @Path values. A typical root resource class in JAX-RS looks like this below:

package demo.jaxrs.server;

import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

@Path("/customerservice/")
@Produces("application/xml")
public class CustomerService {

    public CustomerService() {
    }

    @GET
    public Customers getCustomers() {
        ......
    }

    @GET
    @Path("/customers/{id}")
    @Produces("application/json")
    public Customer getCustomer(@PathParam("id") String id) {
        ......
    }

    @PUT
    @Path("/customers/{id}")
    @Consumes("application/xml")
    public Response updateCustomer(@PathParam("id") Long id, Customer customer) {
        ......
    }

    @POST
    @Path("/customers")
    public Response addCustomer(Customer customer) {
        ......
    }

    @DELETE
    @Path("/customers/{id}/")
    public Response deleteCustomer(@PathParam("id") String id) {
        ......
    }

    @Path("/orders/{orderId}/")
    public Order getOrder(@PathParam("orderId") String orderId) {
       ......
    }
}

Customer resource class can handle requests starting from /customerservice. When /customerservice requests are matched to this class, its getCustomers() method will be selected. updateCustomer(), deleteCustomer() and addCustomer() are used to serve POST, PUT and DELETE requests starting from /customerservice/customer, while getOrder() method delegates the handling of requests like /customerservice/orders/1 to a subresource locator Order.

The @Produces annotation is used to specify the format of the response. When not available on the resource method, it's inherited from a class, and if it's not available on the class then it's inherited from a corresponding message body writer, if any. Default value is */*, but it's recommended that some definite value is specified. The same applies to @Consumes, except that it's the message body readers that are checked as the last resort.

For example, getCustomers() method inherits @Produces annotation from its class, while getCustomer() method overrides it with its own value.

@Path

The @Path annotation is applied to resource classes or methods. The value of @Path annotation is a relative URI path and follows the URI Template format and may include arbitrary regular expressions. When not available on the resource method, it's inherited from a class. For example:

@Path("/customers/{id}")
public class CustomerResource {

    @GET
    public Customer getCustomer(@PathParam("id") Long id) {
        ......
    }

    @GET
    @Path("/order/{orderid}")
    public Order getOrder(@PathParam("id") Long customerId, @PathParam("orderid") Long orderId) {
        ......
    }

    @GET
    @Path("/order/{orderid}/{search:.*}")
    public Item findItem(@PathParam("id") Long customerId, 
                         @PathParam("orderid") Long orderId,
                         @PathParam("search") String searchString,
                         @PathParam("search") List<PathSegment> searchList) {
        ......
    }
}

This example is similar to the one above it, but it also shows that an {id} template variable specified as part of the root @Path expression is reused by resource methods and a custom regular expression is specified by a findItem() method (note that a variable name is separated by ':' from an actual expression).

In this example, a request like 'GET /customers/1/order/2/price/2000/weight/2' will be served by the findItem() method.
List<PathSegment> can be used to get to all the path segments in 'price/2000/weight/2' captured by the regular expression.

More information about Path annotations can be found from JAX-RS spec section 2.3.

HTTP Method

The JAX-RS specification defines a number of annotations such as @GET, @PUT, @POST and @DELETE. Using an @HttpMethod designator, one can create a custom annotation such as @Update or @Patch. For example :

package org.apache.cxf.customverb;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@HttpMethod("PATCH")
public @interface PATCH { 
}

Default Http Method

CXF 3.0.4 introduces a new extension, a org.apache.cxf.jaxrs.ext.DefaultMethod annotation. It can be used to match arbitrary HTTP methods on a single resource method. This can be used in some advanced scenarious for integrating the CXF JAX-RS runtime into non-JAX-RS environments as well as in cases where it is awkward/difficult to have every HTTP method listed for a given path segment. CXF users need to be aware using this option will lead to a non-portable JAX-RS code.


Return types

Either javax.ws.rs.core.Response or custom type can be returned. javax.ws.rs.core.Response can be used to set the HTTP response code, headers and entity. JAX-RS MessageBodyWriters (see below) are in charge of serializing the response entities, those which are returned directly or as part of javax.ws.rs.core.Response.

Response Streaming 

JAX-RS StreamingOutput

StreamingOutput can be used to stream the data to the client, for example:

@GET
@Path("/books/pdf")
@Produces("application/pdf")
public StreamingOutput getPdf() {
    return new StreamingOutput() {
        public void write(final OutputStream os) {
           // read chunks of data from PDF and write to OutputStream
        }  
    };
}

CXF StreamingResponse

CXF 3.0.0 introduces StreamingResponse extension. It can be used with the WebSocket transport or as a possible replacement for the code working with StreamingOutput.

For example, consider that a number of resources need to be returned as they become available:

@GET
@Path("/books")
@Produces("application/xml")
public StreamingResponse<Book> getBooks() {
    return new StreamingResponse<Book>() {
        public void writeTo(final Writer<Book> writer) {
           writer.getEntityStream().write("<Books>".getBytes());
           writer.write(new Book("CXF"));
           writer.write(new Book("JAX-RS"));
           writer.getEntityStream().write("</Books>".getBytes());
        }  
    };
}

Exception handling

One can either throw an unchecked WebApplicationException or return Response with a proper error code set.
The former option may be a better one when no JAX-RS types can be added to method signatures.

For example :

@Path("/customerservice/")
public class CustomerService {

    
    @PUT
    @Path("/customers/{id}")
    public Response updateCustomer(@PathParam("id") Long id, Customer customer) {
        return Response.status(errorCode).build();
    }

    @POST
    @Path("/customers")
    public Customer addCustomer(Customer customer) {
        throw new WebApplicationException(errorCode);
    }

}

Yet another option is to register an ExceptionMapper provider. Ex :

public BookExceptionMapper implements ExceptionMapper<BookException> {
    public Response toResponse(BookException ex) {
        // convert to Response
    }
}

This allows for throwing a checked or runtime exception from an application code and map it to an HTTP response in a registered provider.

Have a look please at this exception mapper which converts Spring Security exceptions into HTTP 403 error code for another example.

Note that when no mappers are found for custom exceptions, they are propagated to the underlying container as required by the specification where they will typically be wrapped in ServlerException, eventually resulting in HTTP 500 status being returned by default. Thus one option for intercepting the exceptions is to register a custom servlet filter which will catch ServletExceptions and handle the causes.

This propagation can be disabled by registering a boolean jaxrs property 'org.apache.cxf.propagate.exception' with a false value. If such property is set and no exception mapper can be found for a given exception then it will be wrapped into an xml error response by the CXF XMLFaultOutInterceptor.

One can also register a custom CXF out fault interceptor which can handle all the exceptions by writing directly to the HttpServletResponse stream or XMLStreamWriter (as XMLFaultOutInterceptor does). For example, see this test interceptor.

Mapping exceptions thrown from CXF interceptors

Starting from CXF 2.7.8 it is also possible to use registered ExceptionMappers to map the exceptions thrown from CXF server in interceptors which are registered after JAXRSInInterceptor (Phase.UNMARSHAL) and out interceptors registered before JAXRSOutInterceptor (Phase.MARSHAL).
In earlier CXF versions such exceptions are only possible to handle with CXF fault in interceptors.

In order to get the exceptions thrown from CXF in interceptors mapped, set a "map.cxf.interceptor.fault" contextual property to true - needed in CXF 2.7.8 to ensure existing in fault interceptors are not affected; the mapping is done by default starting from CXF 3.0.0.

In order to get the exceptions thrown from CXF out interceptors mapped, add org.apache.cxf.jaxrs.interceptor.JAXRSOutExceptionMapperInterceptor to the list of out interceptors.

Customizing default WebApplicationException mapper

CXF ships a WebApplicationException mapper, org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper. By default it logs a stack trace at a warning level and returns Response available in the captured exception.
It can be configured to log a stack trace at a trace level, by setting a 'printStackTrace' property to 'false'. Alternatively, if org.apache.cxf.logging.FaultListener is registered (as a contextual property) and indicates that it handled a given exception, then no more logging is done.

A simple text error message can also be optionally reported, by setting an 'addMessageToResponse' property to 'true', example:

<bean id="exceptionMapper" class="org.apache.cxf.jaxrs.impl.WebApplicationExceptionMapper">
   <property name="addMessageToResponse" value="true" />
</bean>


Note that the custom WebApplicationExceptionMapper, if registered, will be preferred to the default one in normal cases. However, the default WebApplicationExceptionMapper will win over custom WebApplicationExceptionMapper which are less specific (ex, RuntimeException mappers) but which expect to catch WebApplicationException.  And there is a property "default.wae.mapper.least.specific" is introduced to ensure a CXF custom WebApplicationExceptionMapper can still win over in this specific case.

Dealing with Parameters

PathParam annotation is used to map a given Path template variable to a method parameter.
For example :

@Path("/customer/{id}")
public class CustomerService {

    
    @PUT
    @Path("{name}")
    public Response updateCustomer(@PathParam("id") Long id, @PathParam("name") String name) {
        ...
    }
}

In this case a template variable id available from a root class annotation is mapped to a parameter of type Long, while a name variable is mapped to a parameter of type String.

@QueryParam, @HeaderParam, @MatrixParam, @FormParam and @CookieParam annotations are also supported.

Note that the parameters, marked with @FormParam annotation, can take the values from the query parameters in case, if request body is already consumed. This is defined in JAX-RS specification due to the filters (Spring security, etc) consuming the body and thus JAX-RS form parameters becoming empty. User can optionally deactivate standard behavior through setting "set.form.parameters.from.http.parameters" message property to false.

Parameters can be of type String or of any type that have constructors accepting a String parameter or stat ic valueOf(String s) methods.
Additionally CXF JAXRS checks for static fromString(String s) method, so types with no valueOf(String) factory methods can also be dealt with:

public enum Gender {
   MALE,
   FEMALE;

   public static Gender fromString(String s) {
       if ("1".equals(s)) {
           return FEMALE;
       } else if ("1".equals(s)) {
           return MALE;
       }  
       return valueOf(s); 
   }
}

@Path("/{g}")
public class Service {

    
    @PUT
    @Path("{id}")
    public Response update(@PathParam("g") Gender g, @PathParam("id") UUID u) {
        ...
    }
}

Note that on the trunk enums with fromValue() factory methods are also supported.

JAX-RS PathSegment is also supported. A sequence of identically named parameters (queries, headers, etc) can be mapped to List or Set or SortedSet.

CXF JAXRS supports ParameterHandler extensions which can be used to deal with method parameters annotated with one of the JAXRS parameter annotations :

public class MapHandler implements ParameterHandler<Map> {
    public Map fromString(String s) {...}
}

@Path("/map")
public class Service {

    
    @PUT
    @Path("/{mapvalue:(.)+}")
    public Response update(@PathParam("mapvalue") Map m, byte[] bytes) {
        ...
    }
}

Note that ParameterHandlers can not be used to deal with parameters representing a message body, "byte[] byte" in this example. MessageBodyReaders have to deal with this task. That said, a given MessageBodyReader implementation can also implement ParameterHandler.

ParameterHandlers can be registered as providers either from Spring or programmatically.
Note that by default the handlers are checked last after all the other options recommended by the JAX-RS specification have been tried.
Starting from CXF 2.5.3 the handlers will always be checked first for java.util.Date and java.util.Locale parameters. Additionally, a "check.parameter.handlers.first" contextual property can be used to get the handlers checked first when the parameters of other types are processed.

All the parameters are automatically decoded. This can be disabled by using @Encoded annotation.
Parameters can have a default value set using a DefaultValue annotation :

    public Response updateCustomer(@DefaultValue("123") @QueryParam("id") Long id, @PathParam("name") String name) { ... }

JAX-RS mandates that only a single method parameter which is not annotated with JAXRS annotations applicable to method parameters is allowed in a resource method. For example :

public Response do(@PathParam("id") String id, String body) {
}

Parameters like 'String body' are expected to represent the request body/input stream. It's the job of JAX-RS MessageBodyReaders to deserialize the request body into an object of the expected type.

It's also possible to inject all types of parameters into fields or through dedicated setters. For example, the first code fragment in this section can be rewritten like this:

@Path("/customer/{id}")
public class CustomerService {

    @PathParam("id")
    private Long id; 
    
    private String name;

    @PathParam("name")
    public setName(String name) {
        this.name = name;
    } 

    @PUT
    @Path("{name}")
    public Response updateCustomer() {
        // use id and name
    }
}

Parameter beans

There's a CXF extension which makes it possible to inject a sequence of @PathParam, @QueryParam, @FormParam or @MatrixParam parameters into a bean. For example:

@Path("/customer/{id}")
public class CustomerService {

    
    @PUT
    @Path("{name}")
    public Response updateCustomer(@PathParam("") Customer customer) {
        ...
    }

    @GET
    @Path("/order")
    public Response getCustomerOrder(@PathParam("id") int customerId, 
                                     @QueryParam("") OrderBean bean,
                                     @MatrixParam("") OrderBean bean) {
        ...
    }

    @POST
    public Response addCustomerOrder(@PathParam("id") int customerId,
                                     @FormParam("") OrderBean bean) {
        ...
    }
}

public class Customer {
   public void setId(Long id) {...}
   public void setName(String s) {...}  
}

public class OrderBean {
   public void setId(Long id) {...}
   public void setWeight(int w) {...}  
}



Note that there's a single @PathParam with an empty value in updateCustomer() - this is an extension bit. The value for a template variable 'id' is injected into Customer.setId(Long id), while the value for 'name' is injected into Customer.setName(String s). The setter methods should have a single parameter, the conversion from the actual value to the parameter instance follows the same procedure as outlined above.

Similarly, in getCustomerOrder(), OrderBean can be injected with corresponding values from a query string like ?id=1&weight=2 or from matrix parameters set as part of one of the path segments : /customer/1/order;id=1;weight=2. Likewise, in addCustomerOrder(), FormParam("") can capture all the values submitted from an HTML form and inject them into OrderBean.

Nested beans are also supported, which among other things, makes it possible to formulate advanced search queries. For example, given the following bean definitions:

class Name {
    String first;
    String last;
}

class Address {
    String city;
    String state;
}

class Person {
    Name legalName;
    Address homeAddr;
    String race;
    String sex;
    Date birthDate;
}

class MyService
{
    @GET
    @Path("/getPerson")
    Person getPerson(@QueryParam("") Person person);
} 

a query like

> /getPerson?sex=M&legalName.first=John&legalName.last=Doe&homeAddr.city=Reno&homeAddr.state=NV

will result in a Person bean being properly initialized and all the search criteria being captured and easily accessible. Note more enhancements are being planned in this area.

Resource lifecycles

The scopes which are supported by default are Singleton and Prototype(per-request).
Note that JAXRS MessageBodyWriter and MessageBodyReader providers are always singletons.

Classes with prototype scopes can get JAXRS contexts or parameters injected at construction time:

@Path("/")
public class PerRequestResourceClass {

   public PerRequestResourceClass(@Context HttpHeaders headers, @QueryParam("id") Long id) {}
}

Classes with singleton scopes can only have contexts injected at the construction time and it is only a CXFNonSpringJaxrsServlet which can do it. In most cases you can have contexts injected as bean properties right after construction time.

See the "Lifecycle management" section for more details.

Overview of the selection algorithm.

The JAX-RS Selection algorithm is used to select root resource classes, resource methods and subresource locators.

Selecting between multiple resource classes

When multiple resource classes match a given URI request, the following algorithm is used :
1. Prefer the resource class which has more literal characters in its @Path annotation.

@Path("/bar/{id}")
public class Test1 {}
@Path("/bar/{id}/baz")
public class Test2 {}
@Path("/foo")
public class Test3 {}
@Path("/foo/")
public class Test4 {}

Both classes match /bar/1/baz requests but Test2 will be selected as it has 9 Path literal characters compared to 5 in Test1. Similarly, Test4 wins against Test3 when a /foo/ request arrives.

2. Prefer the resource class which has more capturing groups in its @Path annotation.

@Path("/bar/{id}/")
public class Test1 {}
@Path("/bar/{id}/{id2}")
public class Test2 {}

Both classes match /bar/1/2 requests and both have the same number of literal characters but Test2 will be selected as it has 2 Path capturing groups (id and id1) as opposed to 1 in Test1.

3. Prefer the resource class which has more capturing groups with arbitrary regular expressions in its @Path annotation.

@Path("/bar/{id:.+}/baz/{id2}")
public class Test1 {}
@Path("/bar/{id}/bar/{id2}")
public class Test2 {}

Both classes match /bar/1/baz/2 requests and both have the same number of literal characters and capturing groups but Test1 will be selected as it has 1 Path capturing groups with the arbitrary regular expression (id) as opposed to 0 in Test2.

Selecting between multiple resource methods

Once the resource class has been selected, the next step is to choose a resource method. If multiple methods can be matched then the same rules which are used for selecting resource classes are applied. Additionally, one more rule is used.

4. Prefer a resource method to a subresource locator method

@Path("/")
public class Test1 {

 @Path("/bar")
 @GET
 public Order getOrder() {...}

 @Path("/bar")
 public Order getOrderFromSubresource() {...}
}

public class Order {

 @Path("/")
 @GET
 public Order getOrder() { return this; }

}

Both getOrderFromSubresource() and getOrder() methods can be used to serve a /bar request. However, getOrder() wins.

Resource methods and media types

Consider this resource class with 2 resource methods :

@Path("/")
public class Test1 {

 @Path("/bar")
 @POST 
 @Consumes("application/json")
 @Produces("application/json")
 public Order addOrderJSON(OrderDetails details) {...}

 @Path("/bar")
 @POST
 @Consumes("application/xml")
 @Produces("application/xml")
 public Order getOrderXML(OrderDetails details) {...}
 
}

Both methods match /bar requests. If in a given request both Content-Type and Accept are set to application/xml then getOrderXML will be selected. If both Content-Type and Accept are set to application/json then getOrderJSON will be chosen instead.

For this specific example, in both cases either JAXB or JSON message body readers and writers will be selected to deserialize the input stream into OrderDetails and serialize Order into the output stream. Message body providers can have @Produces and @Consumes set too, and they have to match those on a chosen resource method.

The above code can be replaced with this one :

@Path("/")
public class Test1 {

 @Path("/bar")
 @POST 
 @Consumes({"application/json", "application/xml"})
 @Produces({"application/json", "application/xml"})
 public Order addOrder(OrderDetails details) {...}

}

Custom selection between multiple resources or operations

The JAX-RS selection algorithm has been designed with a lot of attention being paid to various possible cases, as far as the selection between multiple matching resource classes or methods is concerned.

However, in some cases, users have reported the algorithm being somewhat restrictive in the way multiple resource classes are selected. For example, by default, after a given resource class has been matched and if this class has no matching resource method, then the algorithm stops executing, without attempting to check the remaining matching resource classes.

Starting from CXF 2.2.5 it is possible to register a custom ResourceComparator implementation using a jaxrs:server/jaxrs:resourceComparator element, example:

<!-- JAX-RS endpoint declaration fragment -->
<jaxrs:server address="/">
<!-- Usual elements, like serviceBeans or providers, etc -->

<!-- Custom ResourceComparator -->
<jaxrs:resourceComparator>
   <bean class="org.custom.CustomResourceComparator"/>
</jaxrs:resourceComparator>
</jaxrs:server>

Custom implementations can check the names of the resource classes or methods being compared and given the current request URI they can make sure that the required class or method is chosen by returning either -1 or 1, as needed. If 0 is returned then the runtime will proceed with executing the default selection algorithm. At the moment the easiest way to get to the details such as the current request URI is to create an instance of the CXF JAXRS UriInfoImpl using a constructor accepting a Message.

Note that by the time a custom ResourceComparator is called the provided resource classes or methods have already been successfully matched by the runtime.

For example, the optional HTTP request and URI parameters (query, matrix, headers, cookies) and form parameters do not affect the selection algorithm.
A custom ResourceComparator can be used when this limitation is considered to be problematic. For example, the following shows one such implementation:

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.MultivaluedMap;

import org.apache.cxf.jaxrs.ext.ResourceComparator;
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfo;
import org.apache.cxf.jaxrs.model.OperationResourceInfoComparatorBase;
import org.apache.cxf.jaxrs.model.Parameter;
import org.apache.cxf.jaxrs.utils.HttpUtils;
import org.apache.cxf.jaxrs.util.JAXRSUtils;
import org.apache.cxf.message.Message;

public class QueryResourceInfoComparator extends OperationResourceInfoComparatorBase implements
         ResourceComparator {

     public QueryResourceInfoComparator() {
     }

     @Override
     public int compare(ClassResourceInfo cri1, ClassResourceInfo cri2, Message message) {
         // Leave Class selection to CXF
         return 0;
     }

     @Override
     public int compare(OperationResourceInfo oper1, OperationResourceInfo oper2, Message message) {
         // HTTP method
         final String httpMethod = HttpUtils.getProtocolHeader(message, Message.HTTP_REQUEST_METHOD, HttpMethod.POST, true);
        
         // Check if CXF can make a decision
         final int cxfResult = compare(oper1, oper2, httpMethod);
         if (cxfResult != 0) {
             return cxfResult;
         }

         int op1Counter = getMatchingRate(oper1, message);
         int op2Counter = getMatchingRate(oper2, message);

         return op1Counter == op2Counter
                 ? 0
                 : op1Counter<  op2Counter
                         ? 1
                         : -1;
     }

     /**
      * This method calculates a number indicating a good or bad match between
      * values provided within the request and expected method parameters. A
      * higher number means a better match.
      *
      * @param operation
      *            The operation to be rated, based on contained parameterInfo
      *            values.
      * @param message
      *            A message containing query from user request
      * @return A positive or negative number, indicating a good match between
      *         query and method
      */
     protected int getMatchingRate(OperationResourceInfo operation, Message message) {

         List<Parameter>  params = operation.getParameters();
         if (params == null || params.size() == 0)
             return 0;

         // Get Request QueryParams
         Set<String>  qParams = getParams((String) message.get(Message.QUERY_STRING));

         int rate = 0;
         for (Parameter p : params) {
             switch (p.getType()) {
             case QUERY:
                 if (qParams.contains(p.getName()))
                     rate += 2;
                 else if (p.getDefaultValue() == null)
                     rate -= 1;
                 break;
             // optionally support other parameter types such as headers, etc
             // case HEADER:
             //  break;
             default:
                 break;
             }
         }
         return rate;
     }

     /**
      * @param query
      *            URL Query Example: 'key=value&key2=value2'
      * @return A Set of all keys, contained within query.
      */
     protected Set<String>  getParams(String query) {
         Set<String>  params = new HashSet<String>();
         if (query == null || query.length() == 0)
             return params;

         MultivaluedMap<String, String> allQueries =
             JAXRSUtils.getStructuredParams(query, "&", false, false);
         return allQueries.keySet();
     }
} 

Now consider this code:

@Path("/paramTest")
public class MySimpleService {

      @GET
      public String getFoo(@QueryParam("foo") String foo){
          return "foo:" + foo;
      }

      @GET
      public String getFooBar(@QueryParam("foo") String foo,
@QueryParam("bar") String bar){
          return "foo:" + foo + " bar:" + bar;
      }

  
} 

Using the custom comparator will lead to getFoo() method accepting a single query parameter selected when a request URI has only one query parameter, and getFoo() method accepting multiple query parameters selected when a request URI has at least two query parameters. Further customizations may also be possible.

Context annotations

A number of context types can be injected as parameters, in fields or through dedicated methods.
UriInfo, SecurityContext, HttpHeaders, Providers, Request, ContextResolver, Servlet types (HttpServletRequest, HttpServletResponse, ServletContext, ServletConfig) can be injected.

A CXF-specific composite context interface, MessageContext is also supported which makes it easier to deal with all the supported JAX-RS contexts (and indeed with future ones) and also lets us check the current message's properties.

Example:

@Path("/customer")
public class CustomerService {
    
    @Context 
    private org.apache.cxf.jaxrs.ext.MessageContext mc; 
    @Context 
    private ServletContext sc;
    private UriInfo ui;
    
    @Context
    public void setUriInfo(UriInfo ui) {
        this.ui = ui;
    }

    @PUT
    public Response updateCustomer(@Context HttpHeaders h, Customer c) {
        mc.getHttpHeaders();
    }
}

Note that all types of supported JAX-RS providers such as MessageBodyWriter, MessageBodyReader, ExceptionMapper and ContextResolver, as well as the list of body providers which can be provided by Providers can have contexts injected too. The only exception is that no parameter level injection is supported for providers due to methods of JAXRS providers being fixed.

Note that Providers and ContextResolver are likely to be of interest to message body providers rather than to the actual application code. You can also inject all the context types into @Resource annotated fields.

Custom Contexts

Registering a custom ContextProvider implementation such as SearchContextProvider lets attach Context annotations to arbitrary classes which can be helpful when some of the information representing the current request needs to be optimized or specialized, example:

package resources;
import org.apache.cxf.jaxrs.ext.search.SearchContext;
@Path("/")
public class RootResource {
    @Context
    private  SearchContext sc;
    // the rest of the code
}

and

<jaxrs:server>
  <serviceBeans>
    <bean class="resources.RootResource"/>
  </serviceBeans>
  <jaxrs:providers>
    <bean class="org.apache.cxf.jaxrs.ext.search.SearchContextProvider"/>
  </jaxrs:providers>
</jaxrs:server>

Custom Context implementations may get all the information about the HTTP request from the current CXF message.

URI calculation using UriInfo and UriBuilder

Mapping of a particular URI to a service that returns some resource is straightforward using the @Path annotation. However RESTful services are often connected: one service returns data that is used as the key in another service. Listing entities and accessing a particular entity is a typical example:

@Path("/customers")
public class CustomerService {

    @GET
    public Customers getCustomers() {
        ......
    }

    @GET
    @Path("/{id}")
    public Customer getCustomer(@PathParam("id") String id) {
        ......
    }

}

For this service we can assume that the returned list of customers exposes only basic attributes and more details is returned using the second method which uses the customer id as the key. Something like this:

GET http://foobar.com/api/customers

<customers>
    <customer id="1005">John Doe</customer>
    <customer id="1006">Jane Doe</customer>
    ...
</customers>
    
GET http://foobar.com/api/customers/1005

<customer id="1005">
    <first-name>John</first-name>
    <last-name>Doe</last-name>
    ...
</customer>

How does a client of this service know how to get from list of customers to given customer? A trivial approach would be to expect the client to compute the proper URI. But wouldn't it be better to have the services provide full URIs in the response that can be used directly? This way the client would be more decoupled from the service itself (which may change URI format over time). A client could be provided the following on response, for example:

GET http://foobar.com/api/customers

<customers-list>
    <customer id="1005" url="http://foobar.com/api/customers/1005">John Doe</customer>
    <customer id="1006" url="http://foobar.com/api/customers/1006">Jane Doe</customer>
    ...
</customers-list>

The problem for the service is how to determine these URIs when the paths come from @Path annotations. It gets more complicated as we consider paths with templates (variables) on multiple levels or sub-resources introducing dynamic routing to different URIs.

The core part of the solution is to inject the UriInfo object into method "getCustomers". This helper object allows for extracting useful information about the current URI context, but more importantly allows for getting the UriBuilder object. UriBuilder has multiple appender methods for building the URI for each object; in our case to the stem URI we can append path in multiple ways, providing it as a string (which we actually want to avoid here) or a resource (class or method) to extract the @Path value. Finally UriBuilder must have values bound to its template variables to render the actual URI. This case in action looks like this:

@Path("/customers")
public class CustomerService {

    @GET
    public Customers getCustomers(@Context UriInfo ui) {
        ......
        // builder starts with current URI and has the path of the getCustomer method appended to it
        UriBuilder ub = ui.getAbsolutePathBuilder().path(this.getClass(), "getCustomer");
        for (Customer customer: customers) {
            // builder has {id} variable that must be filled in for each customer
            URI uri = ub.build(customer.getId());
            c.setUrl(uri);
        }
        return customers;
    }

    @GET
    @Path("/{id}")
    public Customer getCustomer(@PathParam("id") String id) {
        ......
    }

}

Annotation inheritance

Most of the JAX-RS annotations can be inherited from either an interface or a superclass. For example:

public interface CustomerService {

    @PUT
    @Path("/customers/{id}")
    Response updateCustomer(@PathParam("id") Long id, Customer customer);

    @POST
    @Path("/customers")
    Customer addCustomer(Customer customer);

}

@Path("/customerservice/")
public class Customers implements CustomerService {

    
    public Response updateCustomer(Long id, Customer customer) {
        return Response.status(errorCode).build();
    }

    public Customer addCustomer(Customer customer) {
        throw new WebApplicationException(errorCode);
    }

}

Similarly, annotations can be inherited from super-classes. In CXF, the resource class will inherit the class-level annotations from both its implemented interfaces and any class it extends.

Sub-resource locators.

A method of a resource class that is annotated with @Path becomes a sub-resource locator when no annotation with an HttpMethod designator like @GET is present. Sub-resource locators are used to further resolve the object that will handle the request. They can delegate to other sub-resource locators, including themselves.

In the example below, getOrder method is a sub-resource locator:

@Path("/customerservice/")
public class CustomerService {

    @Path("/orders/{orderId}/")
    public Order getOrder(@PathParam("orderId") String orderId) {
       ......
    }
}

@XmlRootElement(name = "Order")
public class Order {
    private long id;
    private Items items;

    public Order() {
    }

    public long getId() {
        return id;
    }


    @GET
    @Path("products/{productId}/")
    public Product getProduct(@PathParam("productId")int productId) {
       ......
    }

    @Path("products/{productId}/items")
    public Order getItems(@PathParam("productId") int productId) {
       return this;
    }

    @GET
    public Items getItems() {
       ......
    }

}

A HTTP GET request to http://localhost:9000/customerservice/orders/223/products/323 is dispatched to getOrder method first. If the Order resource whose id is 223 is found, the Order 223 will be used to further resolve Product resource. Eventually, a Product 323 that belongs to Order 223 will be returned. Similarly, the request to http://localhost:9000/customerservice/orders/223/products/323/items will be delivered to the getItems(productId) method.

Note that a subresource class like Order often has no root @Path annotations which means that they're delegated to dynamically at runtime, in other words, they can not be invoked upon before one of the root resource classes is invoked first. A root resource class (which has a root @\Path annotation) can become a subresource too if one of its subresource locator methods delegates to it, similar to Order.getItems(productId) above.

Note that a given subresource can be represented as an interface or some base class resolved to an actual class at runtime. In this case any resource methods which have to be invoked on an actual subresource instance are discovered dynamically at runtime:

@Path("/customerservice/")
public class CustomerService {

    @Path("/orders/{orderId}/")
    public Order getOrder(@PathParam("orderId") String orderId) {
       ......
    }
}

public interface Order {
    @GET
    @Path("products/{productId}/")
    Product getProduct(@PathParam("productId")int productId);
}

@XmlRootElement(name = "Order")
public class OrderImpl implements Order {
    
    public Product getProduct(int productId) {
       ......
    }
}

@XmlRootElement(name = "Order")
public class OrderImpl2 implements Order {
    
    // overrides JAXRS annotations
    @GET
    @Path("theproducts/{productId}/")
    Product getProduct(@PathParam("id")int productId) {...}
}

Static resolution of subresources

By default, subresources are resolved dynamically at runtime. This is a slower procedure, partly due to the fact that a concrete subresource implementation may introduce some JAXRS annotations in addition to those which might be available at the interface typed by a subresource locator method and different to those available on another subresource instance implementing the same interface.

If you know that all the JAXRS annotations are available on a given subresource type (or one of its superclasses or interfaces) returned by a subresource locator method then you may want to disable the dynamic resolution :

<beans>
<jaxrs:server staticSubresourceResolution="true">
<!-- more configuration -->
</jaxrs:server>
</beans>

Note - starting from CXF 2.7.2 the injection of JAX-RS contexts and parameters will also be supported if this property has been enabled.

Message Body Providers

JAX-RS relies on MessageBodyReader and MessageBodyWriter implementations to serialize and de-serialize Java types. JAX-RS requires that certain types has to be supported out of the box.
By default, CXF supports String, byte[], InputStream, Reader, File, JAXP Source, JAX-RS StreamingOutput, JAXB-annotated types with application/xml, text/xml and application/json formats as well as JAXBElement (see below). JAX-RS MultivaluedMap is also supported for form contents.

See also the "Support for data bindings" section below.

Custom Message Body Providers

It's likely that a given application may need to deal with types that are not supported by default. Alternatively, developers may want to provide a more efficient implementation for handling default types such as InputStream.

Here's an example of a custom MessageBodyReader for InputStream:

@Consumes("application/octet-stream")
@Provider
public class InputStreamProvider implements MessageBodyReader<InputStream> {

    
    public boolean isReadable(Class<InputStream> type, Type genericType, Annotation[] annotations, MediaType mt) {
        return InputStream.class.isAssignableFrom(type);
    }

    public InputStream readFrom(Class<InputStream> clazz, Type t, Annotation[] a, MediaType mt, 
                         MultivaluedMap<String, String> headers, InputStream is) 
        throws IOException {
        return new FilterInputStream(is) {
             @Override
             public int read(byte[] b) throws IOException {
                 // filter out some bytes
             }              
        }     
    }
}

and here's an example of a custom MessageBodyWriter for Long objects:

@Produces("text/plain")
@Provider
public class LongProvider implements MessageBodyWriter<Long> {

    public long getSize(Long l, Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
        return -1;
    }

    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mt) {
        return long.class.isAssignableFrom(type) || Long.class.isAssignableFrom(type);
    }

    public void writeTo(Long l, Class<?> clazz, Type type, Annotation[] a, 
                        MediaType mt, MultivaluedMap<String, Object> headers, OutputStream os) 
        throws IOException {
        os.write(l.toString().getBytes());
        
    }

CXF ships some custom providers too, for dealing with Atom (based on Apache Abdera) and XMLObjects. CXF also supports primitive types and their Number friends when text/plain media type is used, either on input or output.

Registering custom providers

Putting @Provider annotation on the provider class is something that should lead to your provider being registered with the runtime. CXF does not support this feature yet.

One can easily register a provider either from Spring configuration or programmatically:

<beans>
<jaxrs:server id="customerService" address="/">
    <jaxrs:serviceBeans>
      <bean class="org.CustomerService" />
    </jaxrs:serviceBeans>

    <jaxrs:providers>
      <ref bean="isProvider" />
      <ref bean="longProvider" />
    </jaxrs:providers>
    <bean id="isProvider" class="com.bar.providers.InputStreamProvider"/>
    <bean id="longProvider" class="com.bar.providers.LongProvider"/>
</jaxrs:server>
</beans>

Note that instead of the older <jaxrs:entityProviders> it's now <jaxrs:providers>. JAX-RS supports different types of providers and having a single <jaxrs:providers> container is in line with the way other JAX-RS implementations discover providers by checking for @Provider annotations only.

See below for a more complete beans.xml definition.

While having @Provider-annotated providers automatically registered is a handy feature indeed, sometimes it might actually be problematic. For example, in a large project user providers from different libraries might clash.

When using the custom configuration (as shown above) provider instances of different types (handling the same format of request/response bodies) or differently configured instances of the same type can be registered with a different jaxrs:server instance. Yet another requirement might be to have only a given jaxrs:server endpoint among multiple available ones to handle requests with a given media type:

<beans>
<jaxrs:server id="customerService1" address="/1">
   <bean id="serviceBean" class="org.CustomerService" /> 

   <jaxrs:serviceBeans>
      <ref bean="serviceBean"/>
   </jaxrs:serviceBeans>

   <jaxrs:providers>
      <bean class="com.bar.providers.InputStreamProvider"/>
   </jaxrs:providers>
</jaxrs:server>
<jaxrs:server id="customerService2" address="/2">
    <jaxrs:serviceBeans>
      <ref bean="serviceBean"/>
    </jaxrs:serviceBeans>

    <jaxrs:providers>
      <bean id="isProvider" class="baz.foo.jaxrsproviders.InputStreamProvider"/>
    </jaxrs:providers>
</jaxrs:server>

<jaxrs:server id="customerService3" address="/3">
    <jaxrs:serviceBeans>
      <ref bean="serviceBean"/>
    </jaxrs:serviceBeans>

    <jaxrs:providers>
      <ref bean="isProvider"/>
    </jaxrs:providers>
</jaxrs:server>


<bean id="isProvider" class="com.bar.providers.InputStreamProvider">
   <property name="limit" value="5"/>
</bean>

</beans>

In this example a single service bean can be accessed through 3 different paths, /1, /2 and /3. InputStream provider is available on all the 3 paths. com.bar.providers.InputStreamProvider is used in 2 cases, while a special InputStream handler baz.foo.jaxrsproviders.InputStreamProvider from another library is also involved in one case.

Customizing media types for message body providers

As explained above, message body providers can play a major part in affecting the way target resource methods are matched. If a method returns a custom type Foo and a MessageBodyWriter<Foo> is available then it will be used only if one of the media types specified in a given request's HTTP Accept header matches or intersects with one of the media types specified by @Produces annotation in a MessageBodyWriter<Foo> implementation. The same applies if a method accepts a custom type Foo, but this time the value of @Consumes in MessageBodyReader<Foo> will be matched against a request's ContentType value.

Sometimes users would like to experiment with media types different to those statically supported by a given message body reader/writer. For example, application/xml might seem a bit too general in some cases and people may want to try some custom/xml type and still use a default JAXB provider.

In such cases it's possible to override the default @Produces and/or @Consumes types supported by a given provider. It only currently works for JAXBElementProvider and JSONProvider, but any custom provider can avail of this support by simply having setter/getter pairs for either produce and/or consume types and the JAXRS runtime will use them instead.
See this example on how to provide custom media types from Spring.

Advanced HTTP

The JAX-RS specification requires support for a number of advanced HTTP features.

JAX-RS Request context object can be used to check the preconditions expressed via headers such as If-Match, If-None-Match, If-Modified-Since and If-Unmodified-Since.

JAX-RS also makes it easier to work with ETag, Vary, CacheControl, Cookie and Set-Cookie headers.

  • No labels

2 Comments

  1. The sample code for resource class is missing an "import" token at line 3.

    Was

    java.util.HashMap;
    import java.util.Map;
    

    Should be

    import java.util.HashMap;
    import java.util.Map;
    

    Overall, this is a great tutorial. Thanks for writing.

  2. Please fix the following mistake located at 'Overview of the selection algorithm. / Selecting between multiple resource classes / 3. Prefer the resource class which has more capturing groups with arbitrary regular expressions in its @Path annotation. / Third line of the example':

    '@Path("/bar/{id}/{bar}/{id2}")' should be replaced by '@Path("/bar/{id}/baz/{id2}")' in order to have the same number of capturing groups.

    Younes