3

There seems to be many examples about creating RESTful clients by Jersey 1.x, but not Jersey 2.0 or above. I referred to other questions and the Jersey's web site, but I still cannot create a client for REST due to the differences between Jersey 2.0 and the previous one. So I'd like to ask some advice.

So far, my coding is like this.

ClientConfig config = new ClientConfig();
Client client = ClientBuilder.newClient(config);
WebTarget target =  client.target("http://localhost:8080/CustomerBack2211/webresources/entities.customer");

Invocation.Builder invocationBuilder = target.request(MediaType.TEXT_XML_TYPE);
Response response = invocationBuilder.get();
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));

This produces 406 error.

However, when I tried to test RESTful service by Glassfish server, the test works properly, and the server side class has its @GET methods having @Produces({"application/xml", "application/json"}). So I don't see why the coding above produces 406 error on a Java application.

(i.e. the client side has @GET methods in the following way)

@GET
@Path("{id}")
@Produces({"application/xml", "application/json"})
public Customer find(@PathParam("id") Integer id) {
    return super.find(id);
}

@GET
@Override
@Produces({ "application/xml"})
public List<Customer> findAll() {
    return super.findAll();
}

Does any of you see what I'm doing wrong, or could you please suggest an example of a RESTful client? Any advice will be helpful...thanks in advance!


In addition, I'd appreciate if you would offer information about how to invoke methods like GET, PUT and DELETE with appropriate parameters. I just needed to put an ID number (i.e. integer values) when I was testing the server side class on Glassfish RESTful test. However, it seems that I need to set "Class" and/or "Entity" values as arguments, but I cannot see any information associated with them on the Jersey website.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
Hiroki
  • 3,893
  • 13
  • 51
  • 90

1 Answers1

3

For the first block of code:

406 means Not Acceptable.

Look at your request() method target.request(MediaType.TEXT_XML_TYPE). From the Javadoc of request() if states

Invocation.Builder request(MediaType... acceptedResponseTypes)

Start building a request to the targeted web resource and define the accepted response media types. Invoking this method is identical to:

webTarget.request().accept(types);

So basically, in your request, you are saying that you will only Accept: text/plain. Now look at your resource methods. Look at the @Produces. None of them "produce" text/plain. It's all json or xml. That's why you get the exception. Change the accept to application/xml (or MediaType.APPLICATION_XML) on the client side, and you should no longer get this error.

For the second question: I'm assuming you mean why does it work when you test it from the browser.

If you send a request from the browser by simply typing in the url, it will send out the request with many Accept types. If you have firebug (for FireFox) or the developer tools (for Chrome), if you send out a request, you will see a header similar to

Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

You can see application/xml in there. Even if application/xml wasn't there, the wild card */* is there, so basically almost all media types are acceptable as a return type when working in the browser.

For your last question:

Look at the API for SyncInvoker, which Invocation.Builder extends from. You will see different overrloaded put and post methods, most of which, as you mentioned accept an Entity.

There are a few different ways to build an Entity, all of which use one of the static methods. Here are some

  • Entity.entity( body, mediaType )
  • Entity.json( body )
  • Entity.xml( body )

And many more (see the Entity link above). But all of these static method return an Entity. So we could do something like

// resource method
@POST
@Consumes(MediaType.APPLICATION_XML)
public Response getResponse(Customer customer) { ... }

// some model class
@XmlRootElement
public class Customer { ... }

// client request
Customer customer = new Customer();
Response response = target.request().post(Entity.xml(customer));

Internally, the Customer will get converted to XML. If you used Entity.json is would get converted to JSON, BUT you need to make sure you have a JSON provider dependency. Jersey will not come with one by default. See more at Support for Common Media Type Representations


Also note, with your method find, when you try and make a request to the method, the request should end with an integer value, as that's the type specified for the {id} path parameter.

unthought
  • 651
  • 1
  • 15
  • 24
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thank you so much for your advice. Now I can understand whay it produced 406. Could you please tell a little more about the last 2 lines of your suggestion? – Hiroki Dec 04 '14 at 09:25
  • I tried to use the "Customer" class by importing "customerEnd.Customer;". This is a package made in the server side, containg the "Customer" class. Importing this package doesn't prodece any error, probably because I added the WAR file of the server side class to this client. However, it poduces error after I build the project. It would be necessary to use this class, so do you see anything I need to do it for importing this package? – Hiroki Dec 04 '14 at 09:30
  • Your `@Path` is `@Path("{id}")`. And in the method you want to get the path segments value for the id. All I'm saying is that the requestio uri should end with that id, for you to be able to use it in the method. Like `http://..../customers/1`, where `1` would be the id passed to the method. – Paul Samsotha Dec 04 '14 at 09:32
  • As for your second comment. I don't really understand what the problem is. I don't know what the error is so I don't know what to tell you. – Paul Samsotha Dec 04 '14 at 09:33
  • Could I confirm what did you mean by saying "domain"? There should be an entity inside "Entity.xml()" However, to be honest, I'm not really sure about what an entity is. According to the link, it "Encapsulates message entity". So, in this case, is it an object of Customer? – Hiroki Dec 04 '14 at 09:33
  • I changed it. It should be the `customer` object – Paul Samsotha Dec 04 '14 at 09:34
  • In REST, the body of the request is called the entity body. The class `Entity` is just a wrapper for this entity for sending requests. The actual `Customer` object that we pass will get converted to the right format based on what type be use the `Entity`, i.e. `.json`, `.xml`. So in the example case, the `customer` object will get converted to xml, then send to the server. The server will get the xml, and unmarshal it back to a `Customer` object before it gets passed to your resource method – Paul Samsotha Dec 04 '14 at 09:35
  • I see. The problem is, however, I cannot create the Customer object, and this is what I wanted to tell you for my second comment. Let me explain briefly. – Hiroki Dec 04 '14 at 09:46
  • I have one web service application project, called BackEnd, which has "customerEnd.Customer" package. Inside this package is a class called "Customer", and this is what I need to create a objet of. – Hiroki Dec 04 '14 at 09:50
  • I have another project (Java application), called FrontEnd, which has "customerFront" package and "customerFront" class. I added "BackEnd"'s WAR file to "FrontEnd" project, so I thought I could import customerEnd.Customer package into "customerFront" class. Importing this package doesn't make any issue, but it produces error when I built the "customerFront" class. Netbeans say that "error: package customerEnd does not exist", even though it was OK before I build this class. – Hiroki Dec 04 '14 at 09:55
  • Ah I see. You could create a .jar for the model class. An use the same jar for both the webapp and the client app. I'm not sure about adding the war to the client project. It doesn't really make sense, and I have never tried it so don't know how it would work. Alternatively you can create the _same_ customer class for the client project. All it's used for is to model the xml (or whatever format). They don't need to be the same Java object, that doesn't matter. All that matters is that the model format is the same. For example if the server `Customer` model has firstName and lastName.. – Paul Samsotha Dec 04 '14 at 09:55
  • .. so should the client model – Paul Samsotha Dec 04 '14 at 09:55
  • Thank you so much for your advice. Did you mean creating a JAR file from a web application? I quickly went thourgh the internat, but it seems that web applications generate WAR, not JAR. (http://stackoverflow.com/questions/23647765/how-to-create-a-jar-file-for-a-web-application-using-netbeans-ide-8-0) In additon, did you mean I need to use the JAR file for both the server and client side? Like, adding the JAR into the server side? – Hiroki Dec 04 '14 at 10:16
  • To be honest, I'm a little confused about "creating a .jar for the model" and use the jar for the both side. I may well create an identical Customer class inside the package of the client java application, I wonder? – Hiroki Dec 04 '14 at 10:18
  • No I just meant create a whole new project that just consists of your model classes (i.e. `Customer`). Add _that_ projects jar to both the webapp and the client app, so you don't have to create duplicate model classes for both the client and webapp. Trying to create a jar from your webapp is _not_ a good idea. For one it is not safe. The client should not need to know about the server operations – Paul Samsotha Dec 04 '14 at 10:18
  • And again, like I said, the project do not need to use the same Java types, meaning the server could be `com.server.Customer` and the client could be `com.client.Customer`. It does not matter. All the customer object is there for is to help model the xml data. It will be serialized to xml. The client doesn't need to know anything about `com.server.Customer` and the server needs to know nothing about `com.client.Customer`. In many cases when working with the client API, we _don't know anything about any server side objects. We just know the xml format, and we create our own classes to model it – Paul Samsotha Dec 04 '14 at 10:22
  • OK, Thank you! Now I'm examinig your suggestions. I made an identical Customer class as a normal java class by using EJB. Then, I added the JAR of it into the both server and client side. As a result, the client class doesn't produce compile error. Still, the client produces 400 error. Also, I don't see why I should add the JAR into the server side, since the server side doesn't need to use it, I guess. – Hiroki Dec 04 '14 at 11:53
  • I don't get what EJB has to do with anything. Just leave the `Customer` class the way you had it originally before any of this. In the client project create a `Customer` class and copy and paste the code from the server side `Customer`. Forget the extra jar as it seems to be giving you problem. – Paul Samsotha Dec 04 '14 at 11:56
  • It might be impossible to create the Customer by copying and pasting the code from the server side Customer, because the Customer needs packages like "javax.persistence" and etc. These packages seems to come from Java EE, so I used EJB, which also produces JAR. My client is a normal Java application, and it might not have those packages. – Hiroki Dec 04 '14 at 12:07
  • Sorry, it was 405 error, not 400... This was produced in this way. Response res = target.request().put(Entity.xml(cust)); System.out.println(res.getStatus()); – Hiroki Dec 04 '14 at 12:09
  • Forget all the annotations and anything else Just create a `Customer` class with the same fields and getters and setters. Annotate the class with `@XmlRootElement`. Same with the server side `Customer` class. Please remember what I said. We just need to model the XML data. That's IT!. We don't need any persistence annotations. If you need that for the server side, that's fine. But for the client, all we want to do is model the data. We don't need any persistence or anything. – Paul Samsotha Dec 04 '14 at 12:14
  • And a Response status can mean anything. 405 doesn't give me any information about there actual error that causes it. – Paul Samsotha Dec 04 '14 at 12:16
  • Thank you so much for your help... now my client works and I've learned a lot!! – Hiroki Dec 05 '14 at 09:49