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/classesorWEB-INF/liband 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/2006color=blackis 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
@DefaultValueis used to supply a default value for parameter@Encodeis 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
Responseentity 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
Responseto provide metadata; useResponseBuilder;
GenericEntityEntity property of GenericEntity -mapped-> entity body
if (GenericEntity == null) { status_code = 204; } if (GenericEntity != null) { status_code = 200; } }}}
Otherreturned 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
}
// finishedexceptionProvider = 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
ServletExceptionJAX-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
@ContextProviders 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
MessageBodyReaderannotate 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 ProcessingExceptionMessageBodyWriter
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 ProcessingExceptionMedia 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 networkFilters 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?