Generating a JAX-RS generic response

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.

Response builder

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();
}

Response status

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

ACCEPTED

202

Request has been accepted, but the processing has not been completed.

BAD_GATEWAY

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).

BAD_REQUEST

400

The server due to malformed syntax cannot understand the request. The client should not repeat the request.

CONFLICT

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.

CREATED

201

The request was successful and the new resource was created.

FORBIDDEN

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.

GONE

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.

HTTP_VERSION_NOT_SUPPORTED

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.)

INTERNAL_SERVER_ERROR

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.

LENGTH_REQUIRED

411

The server refuses to accept the request without a defined Content-Length value in the HTTP headers. (Since JAX-RS 2.0.)

METHOD_NOT_ALLOWED

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.

MOVED_PERMANENTLY

301

The requested resource has been assigned a new permanent URI and any other references to this resource should use the new URI.

NO_CONTENT

204

The server fulfilled the request, but does not need to return an entity body.

NOT_ACCEPTABLE

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.

NOT_FOUND

404

The server has not found anything matching the Request URI. (Since JAX-RS 2.0.)

NOT_IMPLEMENTED

501

The server does not support the functionality required to complete the request. (Since JAX-RS 2.0.)

NOT_MODIFIED

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.

OK

200

The request was successful.

PAYMENT_REQUIRED

402

The server blocked this request, because commercial payment is required (JAX-RS 2.0.)

PRECONDITION_FAILED

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.)

PROXY_AUTHENTICATION_REQUIRED

407

The client did not first authenticate itself with the proxy (JAX-RS 2.0.)

REQUEST_TIMEOUT

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.

REQUEST_URI_TOO_LONG

414

The server refuses to service request, because the Request-URI is longer than the server is willing to interpret (JAX-RS 2.0.)

REQUEST_RANGE_NOT_ SATISFIABLE

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.)

SEE_OTHER

303

This is HTTP redirection that informs the client to make an alternative GET method on an alternative URI.

SERVICE_UNAVAILABLE

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.

TEMPORARY_REDIRECT

307

The requested resource resides temporarily under a different URI.

UNAUTHORIZED

401

The requested resource requires user authorization.

UNSUPPORTED_MEDIA_TYPE

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.

Generic entities

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.

Return types

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.

  • A void results in an entity body with a (NO_CONTENT) 204 status code.
  • A Response results in entity body mapped from the entity property inside. If the entity property is null then this generates a (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.
  • A 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.
  • For all other Java types, the runtime generates a status code if it is possible to map the result to a known 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.

Tip

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

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.236.27