JAX-RS
JAX-RS is a specification, a set of interfaces and annotations offered by Java EE. Well-known Implementations are RESTEasy and Jersey.
Applications
JAX-RS application has [1..] resources and [0..] providers.
Configuration: via application-supplied subclass of Application
.
Publications
Applications are published in diff ways:
if app runs in Java SE environment
within container (is packed as .war file)
app classes are packaged in
WEB-INF/classes
orWEB-INF/lib
and required libs are packaged inWEB-INF/lib

Resources
Is a POJO that has at least one method annotated with @Path
or a request method designator.
By default a new resource class instance is created for each request to that resource.
Resource methods
JAX-RS resource method designators: @GET, @POST, @PUT, @DELETE, @PATCH, @HEAD, @OPTIONS
Methods must be public
.
Method parameters are mapped from the request according to the semantics of annotation:
@MatrixParam - extracts the value of URI matrix parameter
Matrix parameters are name-value pairs embedded within the path of a URI string
e.g.
http://example.cars.com/mercedes/e55;color=black/2006
color=black
is matrix parameter
@QueryParam - extracts the value of URI query parameter
@PathParam - extracts the value of URI template parameter
@CookieParam - extracts the value of a cookie
@HeaderParam - extracts the value of header
@FormParam
@Context - injects an instance of supported resource. ⚠️ TODO investigate later
@DefaultValue
is used to supply a default value for parameter@Encode
is used to disable auto URI decoding of param value
Return type
void
- results in an empty entity body with 204 status code204 status code means that the request was received and understood, but that there is no need to send any data back
Response
entity property of Response -mapped-> entity body
status property of Response -mapped-> status code
if (Response == null) { status_code = 204; } if (Response.status is NOT set) { if (Response.entity != null) { status_code = 200; } if (Response.entity == null) { status_code = 204; } }
use
Response
to provide metadata; useResponseBuilder
;
GenericEntity
Entity property of GenericEntity -mapped-> entity body
if (GenericEntity == null) { status_code = 204; } if (GenericEntity != null) { status_code = 200; } }}}
Other
returned instance -mapped-> entity body
if (class is anonymous inner class) { use super class; }
if (return_value == null) { status_code = 204; } else { status_code = 200; }

To illustrate consider a method that always returns an instance of ArrayList<String> (directly or wrapped).

Exceptions
// map WebApplicationException to response
exceptionProvider = exceptionMappingProvider<WebApplicationException>
if (!exception.response.contains(entity) && exceptionProvider.isAvailable()) {
Response instance = exceptionProvider.create();
} else {
use exception.response directly
}
// finished
exceptionProvider = exceptionMappingProvider<? super CurrentException>
if (exceptionProvider.isAvailable()) {
try {
Response instance = exceptionProvider.create();
} catch () {
return server_error_response(status code 500) to client
}
}
if (unchecked_exception not_mapped) {
re-throw and propagate to underlying container
}
if (checked_exception not_mapped
&& checked_exception cannot_be_thrown_directly) {
wrappedException = container_specific_exception.wrap(checked_exception)
throw to underlying container
}
Servlet-based implementation wrapper
ServletException
JAX-WS Provider-based implementation wrapper
WebServiceException
HEAD and OPTIONS
// how HEAD requests are handled
if (method.annotatedWith(HEAD) is present) {
call method
} else {
call method.annotatedWith(GET)
discard returned entity
// Performance risk if entity creation is expensive
}
// how OPTIONS requests are handled
if (method.annotatedWith(OPTIONS) is present) {
call method
} else {
generate response using metadata provided by JAX-RS annotations
on matching class and its methods
}
URI Templates
@Path("widgets/{id}")
public class Widget {
... }
💡 This is a relative path with a combination of deployment context and application path @ApplicationPath
.
@Path("widget list/{id}")
equivalent to @Path("widget%20list/{id}")
(encoded automatically).
💡 Annotation value can be a regex, e.g.
@Path("widgets/{path:.+}")
public class Widget {
... }
any request starting with widgets
and containing at least one more path segment will match. E.g. widgets/small/a
=> the value of path will be small/a
Sub Resources
Methods of resource class annotated by @Path
are either:
sub-resource methods => handle HTTP request directly
sub-resource locators => return an object or class that will handle HTTP request
may have all the same parameters as a normal resource method, except they must not have entity parameter
@Path("widgets")
public class WidgetsResource {
@GET
@Path("offers")
public WidgetList getDiscounted() {...} // sub-resource method
@Path("{id}")
public WidgetResource findWidget(@PathParam("id") String id) {
return new WidgetResource(id);
}
}
public class WidgetResource {
public WidgetResource(String id) {...}
@GET
public Widget getDetails() {...}
}
Media type capabilities
@Consumes
@Produces
Can be applied to resource method
(overrides specified on class and on provider), resource class
, entity provider
.
💡 Default is "*/*"
@Path("widgets")
@Produces("application/widgets+xml")
public class WidgetsResource {
@GET
public Widgets getAsXML() {...}
// response media type is "application/widgets+xml", taken from WidgetsProvider
@GET
@Produces("text/html")
public String getAsHtml() {...}
// String containg "text/html" will be returned, written by default impl of
// MessageBodyWriter<String>
@POST
@Consumes("application/widgets+xml")
public void addWidget(Widget widget) {...}
// Value of widget will be mapped from request entity using WidgetProvider
}
@Provider
@Produces("application/widgets+xml")
public class WidgetsProvider implements MessageBodyWriter<Widgets> {...}
@Provider
@Consumes("application/widgets+xml")
public class WidgetProvider implements MessageBodyReader<Widget> {...}
Multiple media types
When accepting multiple media types client may indicate preference by using relative quality factor q parameter
(q-value
). Q-value
is [0..1]
0 - undesired
1 - highly desired (default value)
Example for above code: GET with header Accept: "text/html; q=1, application/widgets+xml; q=0.8" will match getAsHtml
instead of getAsXML
because of q.
A server can also indicate media type preference using the qs
parameter.
@Path("widgets2")
public class WidgetsResource2 {
@GET
@Produces("application/xml; qs=1", "application/json; qs=0.75")
public Widgets getWidget() {...}
}
With GET
request with header Accept: "application/*; q=0.5" => media type application/xml
is selected due to higher qs
value.
if (@Produces.value != request Accept header) {
sub-resource method is not invoked
}
if (@Consumes.value != request Content-Type header) {
sub-resource method is not invoked
}
Annotation inheritance
⚠️ For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.
public interface ReadOnlyAtomFeed {
@GET @Produces("application/atom+xml")
Feed getFeed();
}
@Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
public Feed getFeed() {...}
}
ActivityLog.getFeed()
inherits @GET
and @Produces
from the interface.
⚠️ But, conversely
@Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
@Produces("application/atom+xml")
public Feed getFeed() {...}
}
In the above case @GET annotation from ReadOnlyAtomFeed.getFeed() is not inherited. Because
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored
Providers
Annotation @Provider
is used by JAX-RS runtime to automatically discover provider classes via mechanisms such as class scanning.
Provider must have public constructor
Constructor may include parameter annotated with
@Context
Providers may be annotated with
@Priority
.@Priority(1)
>@Priority(10)
Default priority for application-supplied providers is
javax.ws.rs.Priorities.USER
/**
* User-level filter/interceptor priority.
*/
public static final int USER = 5000;
MessageBodyReader
Message entity body -> Java type
implement
MessageBodyReader
annotate with
@Provider
(for automatic discovery)
MessageBodyReader
always operate on decoded HTTP entity body (decoded by container or JAX-RS runtime).
How entity body is mapped to Java method parameter:
var media_type = request.Content_Type
if (media_type == null) {
media_type = application/octet_stream
}
-----------------
Identify Java type of sub-resource method parameter
-----------------
Select set of MessageBodyReader that support media_type
-----------------
for (MessageBodyReader reader : set) {
if (reader.isReadable()) {
if (more readers suitable) {
choose reader with highest priority
reader.readFrom(entity_body)
done
}
}
}
server throw NotSupportedException (415 status) + no entity
client generate ProcessingException
MessageBodyWriter
javax.ws.rs.ext.MessageBodyWriter
Contract for a provider that supports the conversion of a Java type to a stream.
A MessageBodyWriter
implementation may be annotated with @javax.ws.rs.Produces
to restrict the media types for which it will be considered suitable.
Providers implementing MessageBodyWriter
contract must be either programmatically registered in a JAX-RS runtime or must be annotated with @javax.ws.rs.ext.Provider
annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.
Steps for mapping return value to message entity body:
var object = what_will_be_mapped_to_message_entity_body()
if (object instanceof <? extends Response>) {
object = Response.entity
} else {
object
}
-----------------
var media_type = determine()
-----------------
Select set of MessageBodyWriter that support object and media type of message body
-----------------
sort set (TODO criteria)
-----------------
for (MessageBodyWriter writer : set) {
if (writer.isWriteable()) {
if (more writers suitable) {
choose writer with highest priority
writer.writeTo()
done
}
}
}
server generates InternalServerErrorException (status 500) + no entity
client generate ProcessingException
Media type capabilities
Readers and writers may restrict media types they support using @Consumes
and @Produces
. Absence of annotation means "*/*"
.
ExceptionMapper
must implement javax.ws.rs.ext.ExceptionMapper
Contract for a provider that maps Java exceptions (checker or runtime) to javax.ws.rs.core.Response
. If >1 exception providers are applicable => use with highest priority.
Providers implementing ExceptionMapper
contract must be either programmatically registered in a JAX-RS runtime or must be annotated with @javax.ws.rs.ext.Provider
annotation to be automatically discovered by the JAX-RS runtime during a provider scanning phase.
Client API
javax.ws.rs.client
Filters
Filters execute code at an extension point but without wrapping a method invocation.
@Provider
public interface ClientRequestFilter { // for client
void filter(ClientRequestContext requestContext) throws IOException;
}
// before HTTP request is delivered to the network
public interface ClientResponseFilter { // for client
void filter(ClientRequestContext requestContext,
ClientResponseContext responseContext) throws IOException;
}
// is executed upon receiving a server response
public interface ContainerRequestFilter { // for server
void filter(ContainerRequestContext requestContext) throws IOException;
}
// is executed upon receiving a request from a client
public interface ContainerResponseFilter { // for server
void filter(ContainerRequestContext requestContext,
ContainerResponseContext responseContext) throws IOException;
}
// before HTTP response is delivered to the network
Filters are grouped in Filter chain
. There is a separate filter chain for each extension point (e.g. ClientRequest
, ClientResponse
, ContainerRequest
, ContainerResponse
, PreMatchContainerRequest
).
ContainerRequestFilter
annotated by @PreMatching executed upon receiving client request but before a resource method is matched.
Interceptors
Entity interceptors wrap around a method invocation at a specific extension point.
@Provider
public interface ReaderInterceptor {
Object aroundReadFrom(ReaderInterceptorContext context)
throws java.io.IOException, javax.ws.rs.WebApplicationException;
}
Reader interceptor wraps around calls to MessageBodyReader
readFrom().
public interface WriterInterceptor {
void aroundWriteTo(WriterInterceptorContext context)
throws java.io.IOException, javax.ws.rs.WebApplicationException;
}
Writer interceptor wraps around calls to MessageBodyWriter
writeTo().
Binding
Global
A filter/interceptor that has no annotations is assumed to be bound globally => applies to all resource methods in application (well either Provider
or register manually in Application)
Name binding
@Provider
@MyLogged
class LoggingFilter implements ContainerRequestFilter{...}
@Provider
@MyAuthenticated
class AuthenticationFilter implements ContainerRequestFilter{...}
@Path("*/*")
public class MyResource {
@MyLogged @MyAuthenticated
@GET
public String hello() {...}
}
@NameBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyLogged { }
@NameBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(value = RetentionPolicy.RUNTIME)
public @interface MyAuthenticated { }
JAX-RS request processing cycle
Open questions:
How JAX-RS configuration is done?
Last updated
Was this helpful?