JAX-RS provides means to generate response generically. The abstract class javax.ws.js.core.Response
is a contract object, which the developer uses to produce a generic response with metadata for the JAX-RS runtime provider. An application can extend this class directly or it can create an instance of Response object with the nested inner class javax.ws.js.core.Response.ResponseBuilder
. Most applications tend to use the ResponseBuilder
.
We have already seen an illustration of Response
and ResponseBuilder
in the custom UnknownUserException
exception class. Go back and revisit the user registry example in Defining JAX-RS Resources, if you need to study.
The
Response
class has several static methods that create and return a ResponseBuilder
object.
To create an OK response, with HTTP Response code of 200, we can invoke the ok()
method. We can also supply an entity of the response with the entity()
method, which specifies the payload to send back to the client. We can set the MIME content of the entity too.
After configuring the ResponseBuilder
, we then need to actually construct a response, which is sent to the client, by calling the build()
method.
The following code shows a sample class that demonstrates some of the ways to build generic response outputs programmatically:
package je7hb.jaxrs.basic; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; public class SampleResponse { public Response generateSimpleOk() { return Response.ok().build(); } public Response generateSimpleOkWithEntity() { return Response.ok().entity("This is message") .type(MediaType.TEXT_PLAIN_TYPE).build(); } public Response generateSimpleOkWithEntityXml() { return Response.ok().entity("<msg>This ismessage</msg>") .type(MediaType.TEXT_XML_TYPE).build(); } public Response generateSimpleOkWithGermanyLang() { return Response.ok() .language(„de_de") .entity(„<msg>Einfaches boetschaft</msg>") .type(MediaType.TEXT_XML_TYPE).build(); } public Response generateUnauthorisedError() { return Response.status(Response.Status.UNAUTHORIZED) .build(); } public Response generateUnauthorisedWithEntityXml() { return Response.status(Response.Status.UNAUTHORIZED) .entity("<msg>Unauthorised</msg>") .type(MediaType.TEXT_XML_TYPE).build(); } }
In the SampleResponse
class, we just saw, to avoid subtle literal string errors, note how we make use of the javax.ws.rs.core.MediaType
. Static definitions of this class are used to set the MIME content as an argument to the response builder's type()
method.
It is also possible to set the language and the character encoding of the response with methods language()
and encoding()
. Although not shown here, ResponseBuilder
does have more additional methods in order to configure response headers, last modification date, expiration date and time, new cookies, and links for purpose of URI redirection.
MediaType
class defines static constants, such as APPLICATION_JSON_TYPE
, TEXT_PLAIN_TYPE
, and TEXT_HTMLTYPE
. The class also defines String equivalents of these, such as APPLICATION_JSON ("application/json")
, TEXT_PLAIN
("text/plain"
), and TEXT_HTML
("text/html"
).
This, then, is useful for setting the value @Produces
and @Consumes
in JAX-RS resource methods. For instance, we can write the code in the following way:
@GET @Produces(MediaType.TEXT_PLAIN) public String getList() { /* ...*/ return buf.toString(); }
The class javax.ws.js.core.Response.Status
defines a set of enumeration values that correspond to the response code in the HTTP 1.1 communication standard. Refer to the following table:
Enumerated Constant |
Code |
Description |
---|---|---|
|
202 |
Request has been accepted, but the processing has not been completed. |
|
503 |
The server, while acting as a gateway or proxy, received an invalid response from the upstream server whilst attempting to fulfill the client's request (Since JAX-RS 2.0). |
|
400 |
The server due to malformed syntax cannot understand the request. The client should not repeat the request. |
|
409 |
The request could not be completed due to a conflict with the current state of the resource. This is a useful state when two REST requests attempt to update the resource at the same time on a user defined transaction. |
|
201 |
The request was successful and the new resource was created. |
|
403 |
The server understood the request, but it is refusing to fulfill it. This response can be reported to the client to hint that the request is not secure without making it public why the request was denied. |
|
410 |
The request resource is no longer available at the server and no forwarding address is known. Perhaps, REST style for deleting of the resource has already arrived in the inbox and the server knows somehow that resource has flag set: pending for deletion in the next 24 hours or so. |
|
505 |
The server does not support, or refuses to support, the HTTP protocol that was used in the request message. (Since JAX-RS 2.0.) |
|
500 |
The server encountered an expected condition, which prevented it from fulfilling the request. A useful case for this system might be JAX-RS that cannot connect to external dependent service, for example, credit brokerage or order warehouse system. |
|
411 |
The server refuses to accept the request without a defined |
|
405 |
The method in the Request-URI is not allowed for the resource identified by the Request-URI (Since JAX-RS 2.0)—an example of this might be an immutable resource of secure, static, or reputable constant source of information. |
|
301 |
The requested resource has been assigned a new permanent URI and any other references to this resource should use the new URI. |
|
204 |
The server fulfilled the request, but does not need to return an entity body. |
|
406 |
The resource identified by the request is only capable of generating response entities, which have content characteristics that are not acceptable with the headers sent in the request. |
|
404 |
The server has not found anything matching the Request URI. (Since JAX-RS 2.0.) |
|
501 |
The server does not support the functionality required to complete the request. (Since JAX-RS 2.0.) |
|
304 |
If the client performs a conditional GET request and access is allowed, but the document has not been modified, the server should return this error. It is very rare that a REST application will make a conditional GET request. |
|
200 |
The request was successful. |
|
402 |
The server blocked this request, because commercial payment is required (JAX-RS 2.0.) |
|
412 |
The precondition given in one or more of the request-header fields evaluated to be false when it was tested on the server. (Since JAX-RS 2.0.) |
|
407 |
The client did not first authenticate itself with the proxy (JAX-RS 2.0.) |
|
408 |
The client did not produce a request within the time that the server was prepared to wait. (JAX-RS 2.0.)—An easy example is a ticket reservation on an airplane. |
|
414 |
The server refuses to service request, because the Request-URI is longer than the server is willing to interpret (JAX-RS 2.0.) |
|
416 |
The server refuses to process a request, if the value in the Range request-header exceeds the constraints of the selected resource (JAX-RS 2.0.) |
|
303 |
This is HTTP redirection that informs the client to make an alternative GET method on an alternative URI. |
|
503 |
The server is currently unable to handle the request due to temporary overloading or maintenance of the server. The status implies that the temporary condition will be alleviated after some delay. |
|
307 |
The requested resource resides temporarily under a different URI. |
|
401 |
The requested resource requires user authorization. |
|
415 |
The server refuses to service the request, because the entity of the request is in a format not supported by the endpoint. |
ResponseBuilder
has several helpful functions to build a response. The Response
object is supported in both server and client JAX-RS APIs. Users are encouraged to take advantage of the strong type safety by referencing static constants in Java class rather than loose literal strings.
Since Java has generics, how does JAX-RS take care of parameterized collection types? The answer is the runtime requires some help, because of type erasure.
In order to inform the runtime about a generic collection, there is a class, which developers can use, called javax.ws.rs.core.GenericEntity
.
The following code shows a REST style planning resource that illustrates how to return a generic collection to the client.
@Plan("plans") public PlanningResource { @Path("{id}") @GET @Produces(MediaType.APPLICATION_JSON) public Response getPlanList( @PathParam("id") String id ) { List<Plan> plans = findPlanCollectionById(id); Collections.sort( plans new AscendingDateOrderComparator() ); GenericEntity entity = new GenericEntity<List<Plan>>(plans); return Response.ok(entity).build(); } }
The PlanningResource
class has a resource method getPlanList()
, which retrieves a list of business plans from a persistence store in the application. It sorts these Plan
objects into ascending order and then wraps the list collections of plans in a GenericEntity
. The method returns a response with the generic entity.
After the resource method returns the entity, the JAX-RS runtime will then take care of the rest of the response processing. The runtime applies a converter, if it was registered, to map each Plan
entity object into the required media type "application/json"
and the assembled response is sent over the wire to the client.
Resource methods are allowed to return void
, Response
, GenericEntity
, or another Java type. These return types are mapped to the response sent to the client.
void
results in an entity body with a (NO_CONTENT)
204 status code.NO_CONTENT
) 204 status code. If the status property of the Response is not set, the runtime generates a (OK
) 200 status code for the non-null entity code.GenericEntity
results in an entity body mapped from the Entity property. If the return value of the Entity property is not null then the runtime generates a 200 status code. A null value for the Entity properties causes the runtime to generate a 204 status code.MessageBodyWriter
or default converter. If the runtime identifies this result, which is not-null, it returns a 200 status code otherwise it will generate a 204 status code. For an object instance that is an anonymous inner class, the JAX-RS runtime will, instead, use the superclass.It is the responsibility of the developer to supply additional converters beyond the JAX-RS standard. They may do so through the @Provider
annotation in order to register custom filters and entity interceptors.
Converting Entities to JSON
Java EE 7 provides the Java API for JSON Processing (JSON-P) to define a standard library to parse, generate, and query JSON. Out of the box this library does not supply readymade providers to JAX-RS. In the reference implementation under GlassFish, there does appear to be two classes called JsonStructureBodyReader
and JsonStructureBodyWriter
, which act as JAX-RS providers. If you are stuck for choice, alternatively, you can use GSON, which is a JSON library that many developers have had some success with. You will need to write a custom ReadInterceptor
and WriteInterceptor
implementation in order to integrate it into your application.
Hypermedia linking is the ability for REST services to explicitly reference other endpoints in order to allow a client to navigate information. This capability is actually a constraint of fully REST application architecture and the term for it is Hypermedia as the Engine of Application State (HATEOS). The design of HATEOS system implies that a REST client requires only basic knowledge to interact with an application. The best way to understand this is to think of hyperlinks in HTML. A web browser knows that an HTML anchor element is a navigation point to another HTTP resource. If a user clicks on an anchor, this is instruction to surf to the next web page. The engine of application state for a web browser is the uniform access rule to a spider web of Internet HTTP servers. No special protocols are required beyond the norm.
JAX-RS 2.0 supports Hypermedia by allowing a RESTful server endpoint to add special linkage information to the headers of a HTTP Response. The information in the HTTP header is separate to the actual content. So the response can be anything such as JSON or XML or byte stream and the developer can add linkage information to it.
The class javax.ws.rs.core.Response.ResponseBuilder
has a couple of methods link()
and links()
. These methods create instances of javax.ws.rs.core.Link
, which is the class that encapsulates hypermedia links. A link()
accepts a URI that references the target resource and parameter. A parameter is a relative name for the navigation link called rel
or it can be code
.
Link
relations are descriptive attributes that associated with hyperlink and define the semantic meaning of the relationship between the source and destination resources. Link
relations are used in HTML5 as the common cascading style sheet. The following line of code shows the same:
<link href="stylesheets/bootstrap.css" rel="stylesheet" />
In REST and JAX-RS 2.0 the rel
parameter is retained in a hypermedia link. The Link
class adds a title
, type
, and optional of map of key-value parameters.
To understand better, let's adapt the book RESTful endpoint with hypermedia links. We will start with a refactored class as shown in the following code:
@Path("/hyperbooks") public class RestfulBookServiceWithHypermedia { private List<HyperBook> products = Arrays.asList( new HyperBook(101,"Sir Arthur Dolan Coyle","Sherlock Holmes and the Hounds of the Baskervilles"), new HyperBook(102,"Dan Brown","Da Vinci Code"), new HyperBook(103,"Charles Dickens","Great Expectations"), new HyperBook(104,"Robert Louis Stevenson","Treasure Island")); private final JsonBuilderFactory factory; public RestfulBookServiceWithHypermedia() { factory = Json.createBuilderFactory(null); } @GET @Path("{id}") @Produces({"application/json"}) public Response getProduct(@PathParam("id")int id) { HyperBook product = null; for ( HyperBook book: products ) { if ( book.id == id ) { product = book; break; } } if ( product == null) throw new RuntimeException("book not found"); return Response.ok(product.asJsonObject()) .link("http://localhost:8080/order/"+id+"/warehouse", "stock") .build(); } // ... }
In this endpoint RestfulBookServiceWithHypermedia
, we changed the URI from /books
to /hypermedia
for the type in order to avoid a conflict between resources. This class creates JsonBuilderFactory
that we use later. We have given all the hypermedia books a new Java type HyperBook
and they have a unique ID.
The method getProduct()
maps to HTTP GET request and accepts a REST path parameter ID, which references the bookID. The code attempts to look up the product by the ID. If the product does exists, we convert the Hyperbook
instance to a JSON representation with call to asJsonObject()
. We use the JSON-P API from Java EE 7 (See Appendix D, Java EE 7 Assorted Topics for more details).
If it is not found in the list, then a RuntimeException
exception is thrown. The key to the method is link()
call that accepts an URI for the link header and a value for the rel
parameter. The method generates a response header that looks like the following code:
header[Link] = <http://localhost:8080/order/101/warehouse>; rel="stock" header[Date] = Sun, 18 Aug 2013 17:38:33 GMT header[Content-Length] = 105 header[Content-Type] = application/json
The link relation is a navigation to a warehouse note on a particular order that has a rel
name stock and the URI http://localhost:8080/order/101/warehouse
. It is also possible to generate a collection of link headers for a given response. In order to achieve this aim, we need to invoke indirectly Link.Builder
class.
Let's add one method to retrieve all the hypermedia books in our endpoint, the following code will explain how to do just that:
@GET @Produces({"application/json"}) public Response getProductList() { JsonObjectBuilder builder = factory.createObjectBuilder(); JsonArrayBuilder arrayBuilder = factory.createArrayBuilder(); List<Link> links = new ArrayList<>(); for ( HyperBook book: products ) { arrayBuilder.add( book.asJsonObject() ); links.add( Link.fromPath("http://localhost:8080/order/" +book.id + "/warehouse") .rel("stock") .build()); } builder.add("products", arrayBuilder.build()); return Response.ok(builder.build()) .links( links.toArray( new Link[]{})) build(); }
The method getProducts()
maps also HTTP GET request, but without any parameter and returns a JSON array of all products, the hypermedia books. In order to create a collection of link relations, we use ArrayList<Link>
.
For each hypermedia product, we iterate over all of them, we need a link relation builder. The static call Link.fromPath()
instantiates a Link.Builder
instance from a String. From there, we set rel
parameter name using the rel()
method and then obtain a Link
instance by calling build()
.
At the same time when we are creating link relations, we create a JsonArray
object. We obtain the JSON representation of the Hyperbook
instance and add it to the JsonArray
. The final part of the puzzle is, while building the response, the conversion of the ArrayList<List>
to the Link[]
primitive array for the links(Links… )
call.
The output for the HTTP Response headers looks something like this:
header[Link] = <http://localhost:8080/ordering/104/shipment>; rel="ship", <http://localhost:8080/ordering/103/shipment>; rel="ship", <http://localhost:8080/ordering/102/shipment>; rel="ship", <http://localhost:8080/ordering/101/shipment>; rel="ship" header[Date] = Mon, 19 Aug 2013 08:26:01 GMT header[Content-Length] = 314 header[Content-Type] = application/json
As you can observe the Links
HTTP response is actually comma-delimited. The client-side JAX-RS 2.0 delivers this view resembles the following code extract:
@Test public void shouldRetrieveHyperbooks() throws Exception { WebTarget target = ClientBuilder.newClient() .target( "http://localhost:8080/mywebapp/rest/hyperbooks"); Response response = target.request().get(); // ... Set<Link> links = response.getLinks(); assertFalse(links.isEmpty()); for (Link link: links) { System.out.printf("link relation uri=%s, rel=%s ",link.getUri(), link.getRel()); } assertEquals(200, response.getStatus()); }
From the unit test method shouldRetrieveHyperbooks()
, we are using the JAX-RS 2.0 client side API that we will discuss, very soon, in the section. The important point in the code is retrieval of Link
in a Set
collection from the response. The client side can conveniently parse that set of link relations in the instance, which is very useful. From there, we can get access to the URI, parameter rel
name, the type, and other parameters.
The output should appear as the following:
link relation uri=http://localhost:8080/ordering/103/shipment,rel=ship link relation uri=http://localhost:8080/ordering/102/shipment,rel=ship
This covers building a response. Let's now move to the client side.
3.149.236.27