JAX-RS Client API and GlassFish 4 - Logging HTTP messages (TOTD #194)


One of the main features added in JAX-RS 2 is Client API. This API is used to access Web resources and provides integration with JAX-RS Providers. Without this API, the users need to use a low-level HttpUrlConnection to access the REST endpoint.

If the resource looks like:

@Path("/fruit")
public class MyResource {

@GET
public String get() {

then, before this API, the endpoint can be accessed as:

URL url = new URL("http://. . ./webresources/fruit");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setDoInput(true);
conn.setDoOutput(false);
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
    //. . .
}
With this newly introduced API, the endpoint can be accessed as:

Client client = ClientFactory.newClient();
WebTarget target = client.target("http://. . ./webresources/fruit");
String response = target.request().get(String.class);
Client is the entry point to the Client API and is used to build and execute client requests and consume responses returned. The default instance of Client can be obtained by calling ClientFactory.newClient. Using this we can create a WebTarget by specifying URI of the web resource. These targets are then used to prepare client request invocation by specifying additional query or matrix parameters and resolving the URI template for different names. Finally, one of the HTTP methods is invoked on the prepared client.

The fluency of the API hides the complexity but a better understanding of the flow allows to write better code.

All the classes introduced in the specification are available in javax.ws.rs.client package.

Lets take a look at a complete sample. The complete source code can be downloaded here.

For a resource defined as:

@Path("/fruit")
public class MyResource {

@GET
public String get() {
return Database.getAll();
}

@GET
@Path("{name}")
public String get(@PathParam("name")String payload) {
return Database.get(payload);
}

@POST
public void post(String payload) {
Database.add(payload);
}

@PUT
public void put(String payload) {
Database.add(payload);
}

@DELETE
public void delete(String payload) {
Database.delete(payload);
}
}

A Client invoking all the HTTP methods look like:

Client client = ClientFactory.newClient();
WebTarget target = client.target("http://"
+ request.getServerName()
+ ":"
+ request.getServerPort()
+ request.getContextPath()
+ "/webresources/fruit");

// POST
target.request().post(Entity.text("apple"));

// PUT
target.request().put(Entity.text("banana"));

// GET (all)
String r = target.request().get(String.class);

// GET (one)
r = target.path("apple").request().get(String.class);

// DELETE
target.path("banana").request().delete();

// GET (all)
r = target.request().get(String.class);

And here are the message dumps:

INFO: 1 * LoggingFilter - Request received on thread http-listener-1(5)
1 > POST http://localhost:8080/endpoint/webresources/fruit
1 > Content-Type: text/plain
apple

INFO: 2 * LoggingFilter - Response received on thread http-listener-1(5)
2 < 204
2 < Date: Fri, 11 Jan 2013 22:21:23 GMT
2 < Server: GlassFish Server Open Source Edition 4.0
2 < X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)

INFO: 3 * LoggingFilter - Request received on thread http-listener-1(5)
3 > PUT http://localhost:8080/endpoint/webresources/fruit
3 > Content-Type: text/plain
banana

INFO: 4 * LoggingFilter - Response received on thread http-listener-1(5)
4 < 204
4 < Date: Fri, 11 Jan 2013 22:21:23 GMT
4 < Server: GlassFish Server Open Source Edition 4.0
4 < X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)

INFO: 5 * LoggingFilter - Request received on thread http-listener-1(5)
5 > GET http://localhost:8080/endpoint/webresources/fruit

INFO: 6 * LoggingFilter - Response received on thread http-listener-1(5)
6 < 200
6 < Date: Fri, 11 Jan 2013 22:21:23 GMT
6 < Content-Length: 15
6 < Content-Type: text/html
6 < Server: GlassFish Server Open Source Edition 4.0
6 < X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
[apple, banana]

INFO: 7 * LoggingFilter - Request received on thread http-listener-1(5)
7 > GET http://localhost:8080/endpoint/webresources/fruit/apple

INFO: 8 * LoggingFilter - Response received on thread http-listener-1(5)
8 < 200
8 < Date: Fri, 11 Jan 2013 22:21:23 GMT
8 < Content-Length: 5
8 < Content-Type: text/html
8 < Server: GlassFish Server Open Source Edition 4.0
8 < X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
apple

INFO: 9 * LoggingFilter - Request received on thread http-listener-1(5)
9 > DELETE http://localhost:8080/endpoint/webresources/fruit/banana

INFO: 10 * LoggingFilter - Response received on thread http-listener-1(5)
10 < 204
10 < Date: Fri, 11 Jan 2013 22:21:23 GMT
10 < Server: GlassFish Server Open Source Edition 4.0
10 < X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)

INFO: 11 * LoggingFilter - Request received on thread http-listener-1(5)
11 > GET http://localhost:8080/endpoint/webresources/fruit

INFO: 12 * LoggingFilter - Response received on thread http-listener-1(5)
12 < 200
12 < Date: Fri, 11 Jan 2013 22:21:23 GMT
12 < Content-Length: 7
12 < Content-Type: text/html
12 < Server: GlassFish Server Open Source Edition 4.0
12 < X-Powered-By: Servlet/3.0 JSP/2.2 (GlassFish Server Open Source Edition 4.0 Java/Oracle Corporation/1.7)
[apple]

This has been tested on GlassFish 4 build 70 with the following dependencies:

<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.0-m10</version>
<type>jar</type>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-common</artifactId>
<version>2.0-m10</version>
<type>jar</type>
</dependency>

Here are some more pointers to follow:

Provide feedback on Jersey 2 to users@jersey.java.net and JAX-RS specification to users@jax-rs-spec.java.net.


Comments:

Arun, I have a new about this:

I use the glassfish b70 and the demo was OK but when I use glassfish b72 i got this error:

WARNING: StandardWrapperValve[org.sample.endpoint.TestServlet]: Servlet.service() for servlet org.sample.endpoint.TestServlet threw exception
java.lang.NoSuchMethodError: javax.ws.rs.client.Client.configuration()Ljavax/ws/rs/client/Configuration;
at org.sample.endpoint.TestServlet.processRequest(TestServlet.java:84)
at org.sample.endpoint.TestServlet.doGet(TestServlet.java:140)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)

What is the problem?

Jose

Posted by Jose Diaz on January 22, 2013 at 04:44 PM PST #

Jose,

Seems like a new version of JAX-RS API is integrated in GlassFish 4. I'll update the sample in the next couple of days.

Arun

Posted by Arun Gupta on January 22, 2013 at 05:52 PM PST #

Arun, I have one question. What I should modify to be able to receive json in the POST method and use a POJO instead of String?

@POST
@Consumes("application/json")
public void post(Payload payload){
....
}

I try a plain sample but wasn't able using the latest Netbeans dev build and latest Glassfish 4 promoted build.

I open this issue, in case that's a bug
https://java.net/jira/browse/GLASSFISH-20515

Posted by Sebastien Dionne on May 14, 2013 at 06:41 PM PDT #

Sebastien,

https://blogs.oracle.com/arungupta/entry/consuming_and_producing_json_using should help.

Posted by Arun Gupta on May 24, 2013 at 09:45 AM PDT #

I found the problem with my sample. Some issues were assigned in Glassfish team and other dispatched to Netbeans. Netbeans 7.3 dev build had some problems.

The version of Netbeans that I have was able to make it works.

Here the source that work now for json -> pojo without using custom producer/consumer.

Netbeans was able to detect the Jackson feature from Glassfish librairies and generated these methods.

I just have to test if this code is portable to Wildfly.

package org.netbeans.rest.application.config;

import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

/**
*
* @author Sebastien
*/
@ApplicationPath("resources")
public class ApplicationConfig extends Application {

@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
// following code can be used to customize Jersey 2.0 JSON provider:
try {
Class jsonProvider = Class.forName("org.glassfish.jersey.jackson.JacksonFeature");
// Class jsonProvider = Class.forName("org.glassfish.jersey.moxy.json.MoxyJsonFeature");
// Class jsonProvider = Class.forName("org.glassfish.jersey.jettison.JettisonFeature");
resources.add(jsonProvider);
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(getClass().getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
addRestResourceClasses(resources);
return resources;
}

/**
* Do not modify addRestResourceClasses() method.
* It is automatically re-generated by NetBeans REST support to populate
* given list with all resources defined in the project.
*/
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(com.demo.HelloResource.class);
}

}

and here the source code for my jax-rs 2.0 client that I used to test it.

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/

package com.demo;

import java.util.Collection;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

/**
* Jersey REST client generated for REST resource:HelloResource [hello]<br>
* USAGE:
* <pre>
* NewJerseyClient client = new NewJerseyClient();
* Object response = client.XXX(...);
* // do whatever with response
* client.close();
* </pre>
*
* @author Sebastien Dionne
*/
public class NewJerseyClient {
Client client;
WebTarget root;
private static final String BASE_URI = "http://localhost:8080/WebApplication1/resources/hello";

public NewJerseyClient() {
this.client = ClientBuilder.newClient();
this.root = this.client.target(BASE_URI);
}

public void helloJson(Object requestEntity) {
final Entity<Object> entity = Entity.entity(requestEntity, MediaType.APPLICATION_JSON);

Response response = root.request().post(entity, Response.class);
}

public String getHello() {
return root.request(MediaType.TEXT_PLAIN).get(String.class);
}

public void sayHello(Object requestEntity) {
final Entity<Object> entity = Entity.entity(requestEntity, MediaType.TEXT_PLAIN);

Response response = root.request().post(entity, Response.class);
}

public void close() {
client.close();
}

public static void main(String[] args){
NewJerseyClient client = new NewJerseyClient();

User user = new User();
user.name="name1";
user.lastname="lastname2";

client.helloJson(user);

client.close();
}
}

Posted by Sebastien Dionne on May 24, 2013 at 02:54 PM PDT #

Thanks Sebastien Dionne !

I tried to change provider via web.xml but via ApplicationConfig works very well.
The problem was the "array with one element" add root name.

Posted by Helio Frota on September 03, 2013 at 05:29 AM PDT #

Post a Comment:
Comments are closed for this entry.
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today