Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Content Negotiation in JAX-RS 2.0

DZone's Guide to

Content Negotiation in JAX-RS 2.0

To dig dipper into this matter from JAX-RS view point, a simple case scenario is used that I have recently used in one of my projects.

· Java Zone
Free Resource

The single app analytics solutions to take your web and mobile apps to the next level.  Try today!  Brought to you in partnership with CA Technologies

In JAX-RS both client and server can specify what content type they expect to consume or they are meant to produce. Technically speaking content type is the data format. For instance JSON and XML are two most well-known data formats that are commonly used is RESTful web services. This feature helps server and client developers to be more flexible in design and implementation.

Same as HTTP protocol, content types in JAX-RS are expressed as MIME types too. MIME formatting is the standard way to represent and to categorize different content types. For instance the text representing this article is categorized as (text/plain) in MIME.

In JAX-RS, @Produces and @Consumes annotations are used to specify the content types. As the names implies, @Consumes annotation is used to specify what content format/formats the method is expecting and @Produces is what content format/formats the method is expected to produce. At this point it is important to differentiate between data and the data format. Data is the method’s input and output but, data format is how to transform this data into a standard representation. Data transformation often occurs prior or after transmission.

To dig dipper into this matter from JAX-RS view point, a simple case scenario is used that I have recently used in one of my projects. This will make this subject more interesting and understandable.

Consider a very simple CRUD scenario. In this scenario clients send data contents in their preferred format and server consumes that data. At last the server persists the received data into database. In this scenario the server uses an Entity object to persist data into database. Consider the SimpleEntity class:

@Entity
@XmlRootElement
public class SimpleEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "NAME")
    private String name;

    @Column(name = "FAMILY")
    private String family;

    @Column(name = "AGE")
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getFamily() {
        return family;
    }

    public void setFamily(String family) {
        this.family = family;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

@Override
    public String toString() {
        return "SimpleEntity{" + "name=" + name + ", family=" + family + ", age=" + age + '}';
    }

}


SimpleEntity is DAO (Data Access Object) that the server is using to persist and to retrieve data to and from database. This object is part of the server’s internal mechanism to handle and manage data. Clients don’t need to be aware of such mechanisms and objects. This scenario shows how data formatting (transformation) provides a standard mechanism to decouple client and server from each other and keeps everyone happy. The important points of concern are the formatting and the contents.

First it is agreed between Clients and Server that clients can only produce data in JSON and XML formats. By the same token, the server is expected to consumethe data in these two formats only.

To show how JAX-RS handles @Consumes annotation, it is assumed that server has the contract to support client developers with sample data for each formatting. To provide sample data for JSON data structure, server provides the sample/json resource (method):

    @GET
    @Path("sample/json")
    @Produces("application/json")
    public SimpleEntity JSONproducer() {
        SimpleEntity simpleEntity = new SimpleEntity();
        simpleEntity.setName("Sam");
        simpleEntity.setFamily("Sepassi");
        simpleEntity.setAge(31);
        return simpleEntity;
    }

The result will be a JSON structure as follows:

{"name":"Sam","family":"Sepassi","age":31}

And to provide sample data for XML data structure, server provides the sample/xml resource:

@GET
    @Path("sample/xml")
    @Produces("application/xml")
    public SimpleEntity XMLproducer() {
        SimpleEntity simpleEntity = new SimpleEntity();
        simpleEntity.setName("Sam");
        simpleEntity.setFamily("Sepassi");
        simpleEntity.setAge(31);
        return simpleEntity;
    }

The result will be an XML structure as follows:

<simpleEntity>
<name>Sam</name>
<family>Sepassi</family>
<age>31</age>
</simpleEntity>

These two methods are normal java methods. Their return types are the SimpleEntity class that is one of the server’s business objects. By applying the @Produces annotation, the JAX-RS runtime fetches the method’s output, convert the object using the appropriate MessageBodyWriter, and finally construct the response. The burden of type conversion is handled by JAX-RS. Easy and quick. Later these methods can be modified to accept an ID argument and retrieve the corresponding records from database.

To clarify the idea, these methods were implemented separately for each data format. For a more centralizes and scalable design pattern, @Produce annotation provides a feature to combine multiple content formats over a single method. Consider the /sample resource:


    @GET
    @Path("sample")
    @Produces({"application/xml,application/json"})
    public SimpleEntity sampleProducer() {

        SimpleEntity simpleEntity = new SimpleEntity();
        simpleEntity.setName("Sam");
        simpleEntity.setFamily("Sepassi");
        simpleEntity.setAge(31);
        return simpleEntity;

    }

This time the @Producer annotation accepts two MIME types. Client must explicitly provide the preferred content type. HTTP based clients specify preferred content type by setting the Acceptrequest header value. Server identifies the client’s preferred content type, invoke the sampleProducer method and finally converts the payload to the content that the client prefers.

Now what happens if clients don’t specify the preferred content type or specify */* as the Accept header value? In JAX-RS 2.0 there is a concept called “QUALITY FROM SERVER”or ‘qs’ factor. In the above scenario whenever the client doesn’t specify any specific content type or accepts all type of contents, qs factor instructs the server to offer what content type as the default content format. To clarify this concept, the @Producer annotation can be re-written as follows:

@Produces({"application/json qs=0.8; ,application/xml; qs=0.75"})

The qs factor specifies the priority of choosing default content type to server. The qs factor can take values between 0 and 1. A MIME type has the default value of if its corresponding qs value is not explicitly set. MIME type with the has the highest priority and with has the least. So in the above example choosing json format is prioritized because its qs factor value is greater than the other one in the list.

In case that server doesn’t specify the qs value, the client can instruct the server about the priority of preferred contents. Clients can set the “RELATIVE QUALITY FACTOR” or ‘q’ value along with their request to specify the format order in which they prefer to receive the content.

For instance if the Producer annotation stays intact (without applying the qs factor) and the clients set their Accept header values as:

Accept:  application/json, application/xml; q=0.75

The same result will be achieved and the server delivers the contents in json format.

The previous examples show how contents are produced by server and delivered to clients according to the content type contract and quality factors that were defined in the @Producer annotation. The same contract exits for the contents that comes from client to the server. To specify the expected content format, the @Consumes annotation is used.  This time it’s the server that expects to receive request from client either in XML or JSON format. To demonstrate this scenario consider the following code:

  @POST
  @Path("persist")
  @Consumes({"application/xml,application/json"})
  public void sampleProducer(SimpleEntity simpleEntity) {

         System.out.println(simpleEntity);

         //PERSISTING THE simpleEntity

  }

When the server receives the contents from the client, it fetches the appropriate MediatypeProvider, parses the received content and converts it to the object that is specified in the method’s argument. The server returns HTTP 200 OK response when everything is ok and returns HTTP 400 Bad Request message if the data format is not matching the MIME types that the server expects.

Until now the way contents are defined are called static content negotiation. JAX-RS also provides runtime content negotiation. This feature helps to build more flexible and scalable server methods that are easier to maintain and modify. The Variant class represents a content format or MIME type as it’s been mentioned. A list of Variants can be read from external sources (files, databases, etc.) and being checked at runtime.

Consider the following sample. The previous Persist method was modified to support runtime content negotiation.

    @POST
    @Path("sample")
    public Response sampleProducer(SimpleEntity simpleEntity, @Context Request request) {
        List<Variant> reqVariants = Variant.mediaTypes(MediaType.APPLICATION_JSON_TYPE,
                MediaType.APPLICATION_XML_TYPE).build();
        Variant bestVariant = request.selectVariant(reqVariants);
        System.out.println(simpleEntity);
        if (bestVariant == null) {
            return Response.serverError().status(Response.Status.NOT_ACCEPTABLE).build();
        }
        MediaType reqMediaType = bestVariant.getMediaType();
        if (reqMediaType.isCompatible(MediaType.APPLICATION_JSON_TYPE)) {
            System.out.println("Data received in JSON format : ");
            System.out.println(simpleEntity);
            return Response.ok().build();
        } else if (reqMediaType.isCompatible(MediaType.APPLICATION_XML_TYPE)) {
            System.out.println("Data received in XML format : ");
            System.out.println(simpleEntity);
            return Response.ok().build();
        }
        System.out.println("NOT SUPPORTED");
        return Response.serverError().status(Response.Status.NOT_ACCEPTABLE).build();
    }


As it is obvious, to implement runtime content negotiation, more plumping is required.

CA App Experience Analytics, a whole new level of visibility. Learn more. Brought to you in partnership with CA Technologies.

Topics:
java ,javaee ,jax-rs 2.0 ,restful ,http ,glassfish ,application server

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}