JAX-RS 2.1 - the latest & greatest
Version 2.1 (JSR 370) is the current release for the JAX-RS specification (at the time of writing). It is a part of the Java EE 8 Platform as well. This chapter will give you an overview (along with code examples) of all the new features in this release.
Here is what will be covered
Big ticket features in JAX-RS 2.1
Support for
Server Sent Events
(server and client)JSON Binding (
JSON-B
) API integrationNew
Reactive
client API
Other key enhancements
CompletionStage
support in asynchronous server APIExecutorService
support in asynchronous Client APIJSON-P
support enhancement@Priority
for custom providers
Support for Server Sent Events
Now JAX-RS has built-in support for the Server Sent Events standard (SSE) - represented by text/event-stream
media type. JAX-RS defines a server as well as client side API (javax.ws.rs.sse
package) - here is an overview
SseEvent
represents a generic abstraction for a Server Sent EventInboundSseEvent
andOutboundSseEvent
represent incoming and outgoing events respectivelyUse
OutboundSseEvent.Builder
to create instance of anOutboundSseEvent
SseEventSink
can be used to send individualOutboundSseEvent
s and aSseBroadcaster
is used to manage multiple suchSseEventSink
s and create a simpler abstractionA
SseEventSource.Builder
is used to create aSseEventSource
which is a client side handle to process incoming SSE events (InboundSseEvent
s)Last but not the least, we have
Sse
- the API construct which provides factory methods to createSseBroadcaster
andOutboundSseEvent
instances
Instances of
Sse
andSseEventSink
can only be injected using@Context
Server API
On the server side, you can generate SSE events which clients (browser based or programmatic) can consume. Here is the high level flow to generate an event
Create an instance of an
OutboundSseEvent
Send it using a
SseEventSink
Client invokes the endpoint
JAX-RS runtime injects instances of
SseEventSink
andSse
Create an instance of an
OutboundSseEvent
using theSse#newEvent
(factory method)Send it using a
SseEventSink
Close it - this ends the (long lived) connection b/w server and client
Another technique for instantiating an
OutboundSseEvent
is viaSse#newEventBuilder
(another factory method) which allows you to set other events details in addition toname
anddata
. These include -id
,comment
as well as a failure handling strategy usingreconnectDelay
method
There is more!
Sending a single event (or maybe a few) and saying goodbye to the client is fine, but its not what SSE is used for in general. It's used to transmit (real time) information like stock prices, game scores etc. The client is not required to ping/ask/inquire/poll the server repeatedly - instead, the server sends data (whenever available) on the (SSE) channel which is already open. This sounds like broadcast, doesn't it ? Its not a surprise that the JAX-RS API models it using SseBroadcaster
it follows the register (subscribe) and broadcast (publish) paradigm
is used to make it easier to handle multiple SSE clients
Handling Custom types/objects
Information sent using the JAX-RS 2.1 SSE support does not only have to be of type java.lang.String
– it supports Java primitives (Integer
, Long
etc.), JSON-B
& JAX-B
annotated types as well as custom objects whose encoding process (Java object to on-wire format) is defined using a MessageBodyWriter
implementation
In this example
Multiple
OutboundSseEvent
s are created – each differing in the data/media type (text, json, xml etc.)the default SSE media type is
TEXT_PLAIN
, hence does not need to be explicitly specified when dealing withString
data typeEmployee
class is aJSON-B
annotated classCustomer
is aJAX-B
annotated classStudent
has a customMesaageBodyWriter
implementation
Here is the MesaageBodyWriter
implementation for Student
class
... and here is the Student
POJO
Accessing the REST endpoint (e.g. http://localhost:8080/
) will produce an output similar to the following i.e. you will get a SSE event with heterogeneous data types
Client API
You can use the JAX-RS SSE Client API to programatically access other SSE endpoints. The high level flow is as follows
build an instance of
SseEventSource
define callbacks to handle incoming
InboundSseEvent
sopen
the channel and start accepting SSE streams
Let the code be our guide...
create the
WebTarget
instance (regular JAX-RS client code)build
SseEventSource
on top of theWebTarget
instanceRegister callbacks
Callback for a SSE event represented by
InboundSseEvent
instanceCallback for handling error thrown at runtime
Callback for logic to be executed after the events have been received
call
open
- ready to accept eventscontinue listening to events for sometime (10 secs in this case) and then
close
to terminate the SSE channel
Note on Thread safety -
Sse
,SseBroadcaster
,SseEventSink
,SseEventSource
are thread safe
JSON Binding (JSON-B) integration
To complement its JAXB
support, JAX-RS now includes first class support for JSON-B
as well i.e. you can decorate your classes with JSON-B annotations and let the JAX-RS runtime deal with the JSON serialization and de-serialization. This is based on the already established Entity Provider
based feature which supports a variety of Java types (e.g. primitive types, Reader
, File
etc.) including JSON-P
objects (e.g. JsonValue
)
A detailed disussion on JSON-B is out of scope of this book/chapter, but here is a high level overview. I would encourage you to dig into the JSON-B
specification for more details
JSON-B quickie
It's a standard specification which defines a binding (serialization and deserialization) API between Java objects and JSON documents (RFC 7159 compatible)
Default mapping
JSON-B spec defines default mapping of Java classes and instances to equivalent JSON document components. It covers Java primitives (String
, Boolean
, Long
etc.) and other types such as BigInteger
, URL
, Date
etc.
For a simple POJO (Employee
), the JSON-B API can be used as follows (in default mapping mode)
Customized mapping: A bunch of annotations are defined in order to further customize the binding process (example coming up)
Reference Implementation: Yasson is the reference implementation for the JSON-B specification
Here is an example to highlight some of the JSON-B
annotations
@JsonbPropertyOrder
specifies the lexicographical reverse (Z to A) order for JSON attributes i.e.Employee
JSON form will havename
followed byemail
@JsonbProperty
is used to modify the name of the JSON attribute i.e. its not the same as the POJO field/variable name. In this case, the JSON representation forEmployee
will have theemp_email
attribute instead ofemail
@JsonbTransient
tells the JSON-B runtime to ignore (not process) the specific property/field - we ensure that theEmployee
salary
remains a secret!
How will these annotations be used at runtime within a JAX-RS application ? Another example to illustrate this
Employee
is searched - usingemail
as the criteriaOnce found, the POJO representation is returned by the method. Thanks to the JSON-B integration, a JSON representation of
Employee
is returned to the caller
Note: in a situation where an entity can be treated as both JSON-B and JSON-P, the entity providers for JSON-B
will take precedence over those for JSON-P unless the object is of JsonValue
and its sub-types
Reactive Client API
The client API now supports the reactive paradigm using RxInvoker
. Up until now, asynchronous operations were initiated with the help of AsyncInvoker
- here is an example
Chapter Asynchronous JAX-RS has more details
This is great, but its hard to chain
API calls in a pipeline
fashion e.g. do this, and when it finishes, do that - all asynchronously. JAX-RS does provide the InvocationCallback
to handle this - but it's not a great fit for complex logic as it leads to a callback hell
problem
JAX-RS 2.1 ships with CompletionStageRxInvoker
(default implementation of RxInvoker
) which is based on the (JDK 8) CompletionStage
API
Here is what's going on in the above example
created a task (
CompletableFuture
) for searching a user by emailcreated another task to find Github user profile
chained the tasks to
compose
an asynchronous pipelineextract the result - invocation of
result.get()
triggersuserSearch
, followed by the profile search task
Open for extension
Its possible to plugin alternate implementations of RxInvoker
as well
register the new Provider on the Client
declare the invoker in
rx
method call
Others
Here are some of the other smaller but important enhancements to the API
CompletionStage
support in asynchronous server API
CompletionStage
support in asynchronous server APIJAX-RS server side component now has support for returning a CompletionStage
to mark the request as eligible for asynchronous processing - this is an addition to the AsyncResponse
API which has been available since JAX-RS 2.0 (Java EE 7). The advantage this approach has over the AsyncResponse
based API is that it is richer and allows you to create asynchronous pipelines
Let's go over an example to see this in action
It starts with a HTTP GET to
/booking/cabs/<user>
which invokes thegetCab
methodthe method returns a
CompletionStage
and returns immediatelythe thread which served the request is now freed up
and then its about creating the asynchronous pipeline
we orchestrate the tasks for user validation and driver search using
thenComposeAsync
– this gives aCompletableFuture
i.e. thesearchDriverTask
we then supply a
Function
which takes the driver (returned by the above step) and invokes thenotifyUser
method – this is theCompletionStage
which we actually return i.e.notifyUserTask
– this is obviously executed later on, all we did was compose the sequence
once the process is completed (delays are introduced using
Thread.sleep()
), the response is sent back to the user – internally, ourCompletableFuture
finishes
ExecutorService
support in asynchronous Client API
ExecutorService
support in asynchronous Client APIThe asynchronous execution support in Client API via the traditional Future
based option (Invocation.Builder#async
) or the new reactive client (Invocation.Builder#rx
) is enhanced by the ability to define an ExecutorService
which will be used to process these asynchronous tasks. This is possible using the executorService
and scheduledExecutorService
in ClientBuilder
- you end up with a Client
instance whose requests will be executed in the thread pool defined by the executor service
The above is example for a standalone environment where a fixed pool of 10 threads will take care of the submitted tasks (Runnable
, Callable
). If you are in a Java EE environment (Java EE 7
and above), the container managed executor service (Java EE Concurrency Utilities) should be used
Sub Resource locators
Up until now (JAX-RS 2.0), sub-resource locators could only return an object - now it's possible for them to return a class
In the above example, rather than returning instances of our resource classes, the explicit class is returned - the JAX-RS runtime takes care of creating an instance as per existing rules laid out by the specification
Sub-resource locators were discussed in chapter JAX-RS Core Part I
JSON-P
support enhancement
JSON-P
support enhancementJsonString
and JsonNumber
(derivatives of JsonValue
) have been included as the supported sub-type i.e. JAX-RS now has entity providers for them as well
Provider @Priority
@Priority
You can now decorate your custom provider imeplemtations with @Priority
to help JAX-RS runtime choose the appropriate one at runtime - given you have multiple such providers. Points to note
javax.ws.rs.Priorities.USER
is the default valuelower priority will be preferred e.g.
@Priority(2)
will be chosen over@Priority(5)
in a scenario where two or more providers have the same priority, then its upto the implementation to define which gets chosen
Last updated