jeudi 10 mai 2012

RESTful client in Java with JAX-RS 2.0 Client API


On october 21th 2010, I wrote a RESTful client in Java. We are now in 2012, and the next version of JAX-RS (the 2.0 and which will be included as part of Java EE 7) will include a client API.

The JAX-RS 2.0 specification is still work in progress, but a few implementations already exist (in beta or in the first milestones).

To begin to explore the JAX-RS Client API, I decided to use the Jerseyimplementation (which is the JAX-RS's reference implementation) and the same RESTful service that I used in RESTful client in JAVA.
Modification of the RESTful service
The only modification in the service is the addition of the XML support in addition to JSON.
I replaced the line :
@Produces ("application/json")
by :
@Produces({MediaType.APPLICATION_XML,MediaType.APPLICATION_JSON})

I added the XML support because with the JAX-RS 2.0 implementation that I chose, Jersey's milestone 3,  the parsing of the list of object (in my case List<Music>) isn't working with JSON, but it's working with the XML.
[update May 11, 2012] JSON de-/serialization works, read the second comment of Martin Matula

The Client using JAX-RS Client API
The easier way to call the service with the client API using a GET query is to write something like this :

JavaClientRESTFul20.java
  package javaclientrestful20;
  
  import java.util.List;
  import javax.ws.rs.client.Client;
  import javax.ws.rs.core.GenericType;
  import javax.ws.rs.core.MediaType;
  import javax.ws.rs.ext.ClientFactory;
  
  public class JavaClientRESTFul20 {
     
     // curl http://localhost:8080/RESTfulServices/rs/ArtisteNameBeginningBy/Arc
 
     public static void main(String[] args) {
 
         Client client = ClientFactory.newClient();
                 
         GenericType<List<Music>> listm = new GenericType<List<Music>>() {};        
         List<Music> listMusic = client.target("http://localhost:8080/RESTfulServices/rs/ArtisteNameBeginningBy/Arc")
                        .request(MediaType.APPLICATION_XML)
                        .get(listm);
         
         System.out.println("listMusic : "+listMusic);   
     }
 }

But as my service take a parameter from the path, the best way is to write something like this :

JavaClientRESTFul20Extend.java
  package javaclientrestful20;
  
  import java.util.List;
  import javax.ws.rs.client.Client;
  import javax.ws.rs.core.GenericType;
  import javax.ws.rs.core.MediaType;
  import javax.ws.rs.ext.ClientFactory;
  
  public class JavaClientRESTFul20Extend {
     
     // curl http://localhost:8080/RESTfulServices/rs/ArtisteNameBeginningBy/Arc
 
     public static void main(String[] args) {
 
         String artistBeginBy = "Arc";
         //String artistBeginBy = "Arch";
         
         JavaClientRESTFul20Extend clientfindArtistByName = new JavaClientRESTFul20Extend();
         List<Music> listMusic = clientfindArtistByName.findArtistByName(artistBeginBy);
         
         System.out.println("listMusic : "+listMusic);    
     }    
     
     protected List<Music> findArtistByName(String artistBeginBy){
         
         Client client = ClientFactory.newClient();
                 
         GenericType<List<Music>> listm = new GenericType<List<Music>>() {};
         
         List<Music> listMusic = client.target("http://localhost:8080/RESTfulServices/rs/ArtisteNameBeginningBy/")
                        .path("{beginBy}")
                        .pathParam("beginBy", artistBeginBy)
                        .request(MediaType.APPLICATION_XML)
                        .get(listm);
         
         return listMusic;
     }
 }

Note : On internet, I read that you should be able write something like this to pass a path parameter
List<Music> listMusic = client.target
("http://localhost:8080/RESTfulServices/rs/ArtisteNameBeginningBy/{beginBy}")
                       .pathParam("beginBy", artistBeginBy)
                       .request(MediaType.APPLICATION_XML)
                       .get(listm);

But it's not working with Jersey milestone 3.

The class Music used by the client above
Music.java
  package javaclientrestful20;

  import java.io.Serializable;
  import javax.xml.bind.annotation.XmlRootElement;

  @XmlRootElement
  public class Music implements Serializable {

     private Long id;
     private String artisteName;
     private String albumTitle;

     public Music() {
     }

     public String getAlbumTitle() {
         return albumTitle;
     }

     public void setAlbumTitle(String albumTitle) {
         this.albumTitle = albumTitle;
     }

     public String getArtisteName() {
         return artisteName;
     }

     public void setArtisteName(String artisteName) {
         this.artisteName = artisteName;
     }

     public Long getId() {
         return id;
     }

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

     @Override
     public String toString(){

         StringBuilder sb = new StringBuilder();
         sb.append("id : ");sb.append(id);sb.append(" ; ");
         sb.append("artisteName : ");sb.append(artisteName);sb.append(" ; ");
         sb.append("albumTitle : ");sb.append(albumTitle);
         sb.append(" \n");

         return sb.toString();
     }
 }

3 commentaires:

Martin Matula a dit…

Thanks for trying Jersey 2.0-m03 out and posting this blog. Since you mentioned some things did not work for you in m03, I filled the following two issues - hopefully we can fix them soon:

http://java.net/jira/browse/JERSEY-1142
http://java.net/jira/browse/JERSEY-1143

Martin Matula a dit…

JSON de-/serialization works (http://java.net/jira/browse/JERSEY-1142), you just have to make sure jersey-media-json-*.jar and jackson-*.jars are on the classpath and you have to enable the feature on the client by calling client.configuration().enable(new JsonFeature())

Patrick Champion a dit…

Thank you Martin for the solution!
it works when I add the jars :
- jersey-media-json-2.0
- jackson-jaxrs
- jackson-core-asl
- jackson-mapper-asl
- jackson-xc

and the line
client.configuration().enable(new JsonFeature());


I had to add and use a class which wraps the list too.
I will write about that later.

And, it will be great if json was supported natively (without the line client.configuration().enable(new JsonFeature()); )