4

I have a get call to a rest service (that works fine), and I would like to change it and use jersey. I downloaded the last JARs from the official site and put them inside my project. All my response thrown exceptions (except this: String response = invocationBuilder.get(String.class);):

javax.ws.rs.NotAcceptableException: HTTP 406 Not Acceptable
    at org.glassfish.jersey.client.JerseyInvocation.convertToException(JerseyInvocation.java:1014)
    at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:816)
    at org.glassfish.jersey.client.JerseyInvocation.access$700(JerseyInvocation.java:92)
    at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:700)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
    at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:696)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:420)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:316)

The code:

        ClientConfig clientConfig = new ClientConfig();

        Client client = ClientBuilder.newClient(clientConfig);
        //not so orthodox... 
        WebTarget webTarget = client.target(uri);//uri= https://www.foo.bar/api/v1.0/sms/sendMessage?accessKeyId=34TR&idCampaign=4&fromNumber=Test&toNumber=393281234567&message=Inserisci+la+seguente+password%3A+8c495b

        Invocation.Builder invocationBuilder2 =
                webTarget.request(MediaType.APPLICATION_JSON);
        Invocation.Builder invocationBuilder =
                webTarget.request();//------NO MEDIA TYPE SPECIFICATION
        Invocation.Builder invocationBuilder3 =
                webTarget.request(MediaType.APPLICATION_XML_TYPE);
        Invocation.Builder invocationBuilder4 =
                webTarget.request(MediaType.TEXT_XML_TYPE);
        Invocation.Builder invocationBuilder5 =
                webTarget.request(MediaType.TEXT_PLAIN_TYPE);
        Invocation.Builder invocationBuilder6 =
                webTarget.request(MediaType.APPLICATION_FORM_URLENCODED);

        try {
            String response2 = invocationBuilder2.get(String.class);
        } catch (Exception e) {System.out.println(e);}
        try {
    //WORKS ONLY THIS, WITH NO MEDIA TYPE SPECIFICATION
            String response = invocationBuilder.get(String.class); 
        } catch (Exception e) {System.out.println(e);}
        try {
            String response3 = invocationBuilder3.get(String.class);
        } catch (Exception e) {System.out.println(e);}
        try {
            String response4 = invocationBuilder4.get(String.class);
        } catch (Exception e) {System.out.println(e);}
        try {
            String response5 = invocationBuilder5.get(String.class);
        } catch (Exception e) {System.out.println(e);}
        try {
            String response6 = invocationBuilder6.get(String.class);
        } catch (Exception e) {System.out.println(e);}

//NONE OF THIS WORKS (last part of the test):
        try {
        SmsResponse response02 = invocationBuilder2.get(SmsResponse.class);
        } catch (Exception e) {System.out.println(e);}
        try {
        SmsResponse response0 = invocationBuilder.get(SmsResponse.class);
        } catch (Exception e) {System.out.println(e);}
        try {
        SmsResponse response03 = invocationBuilder3.get(SmsResponse.class);
        } catch (Exception e) {System.out.println(e);}
        try {
        SmsResponse response04 = invocationBuilder4.get(SmsResponse.class);
        } catch (Exception e) {System.out.println(e);}
        try {
        SmsResponse response05 = invocationBuilder5.get(SmsResponse.class);
        } catch (Exception e) {System.out.println(e);}
        try {
        SmsResponse response06 = invocationBuilder6.get(SmsResponse.class);
        } catch (Exception e) {System.out.println(e);}

The response string is a JSON: {"status":"success","uid":"407077","numSms":1,"errorMsg":false}

But I get an exception while trying to obtain a SmsResponse object (last part of code), response0 thrown the exception below (the other cases thrown the previous 406 exception):

 org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=application/json;charset=UTF-8, type=class it.sian.zfab.SmsResponse, genericType=class it.sian.zfab.SmsResponse.
    at org.glassfish.jersey.client.JerseyInvocation.translate(JerseyInvocation.java:808)
    at org.glassfish.jersey.client.JerseyInvocation.access$700(JerseyInvocation.java:92)
    at org.glassfish.jersey.client.JerseyInvocation$2.call(JerseyInvocation.java:700)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
    at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:696)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:420)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:316)

The bean:

@XmlRootElement
public class SmsResponse implements Serializable {

    private static final long serialVersionUID = 1L;

    @QueryParam("uid")
    private String uid;

    @QueryParam("status")
    private String status;

    @QueryParam("errorMsg")
    private String errorMsg;

    @QueryParam("numSms")
    private Integer numSms;

    //getter and setter...
}

My old method (that works), here you can see that it's working fine with content application/json:

private <T> T restCallJson(String uri, Class<T> returnType)
{
    T objectResponse = null;
    try {
        URL url = new URL(uri);
        HttpsURLConnection connection = (HttpsURLConnection)url.openConnection();
        connection.setRequestMethod("GET");
        connection.setRequestProperty("Content-Type", "application/json");

        InputStream inputStream = connection.getInputStream();

        Gson gson = new Gson();
        final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        objectResponse = gson.fromJson(reader, returnType);

        connection.disconnect();            
    }
    catch (Exception e)
    {
        log.error("WebServiceUtility - restCallJson: " + e);
    }
    return objectResponse;
}

1) Why cannot I specify the MediaType? Which one should I use?

2) Why cannot I retrieve directly the SmsResponse object?

Accollativo
  • 1,537
  • 4
  • 32
  • 56

1 Answers1

5

1) Why cannot I specify the MediaType? Which one should I use?

request(type) sets the Accept: <type> header . If the server is not set up to produce the type, then the correct response to send is 406 Not Acceptable.

For example request(MediaType.APPLICATION_FORM_URLENCODED) is telling the server you want the data back in application/www-x-form-urlencoded form. If the server cannot produce that media type for the endpoint, you will get back a 406.

If the server can send JSON, then request(MediaType.APPLICATION_JSON) should work.

What I would do to debug, is instead of doing

String reponse = invocationBuilder.get(String.class);

Get the actual Response object, and check out the headers and response body.

Response response = invocationBuilder.get();
int status = response.getStatus();
String body = response.readEntity(String.class);

Apart from debugging, this will avoid the exception from the client.

What this

Invocation.Builder invocationBuilder = webTarget.request();

does is set the Accept header to the wildcard */*, meaning that the server can send whatever type it wants. Usually you don't want this, as the client needs to know the type it's getting back in order to process it. What you can do to debug, is send that wildcard request and get back the response. From there you can see the Content-Type header to see what type the server is sending back

Invocation.Builder invocationBuilder = webTarget.request();
Response response = invocationBuilder.get();
String contentType = response.getHeaderString("Content-Type");

From there you can see what type you should set for the Accept header. But looking from your second stacktrace, it seems the server is sending application/json, so there is no reason why request("application/json") shouldn't work.

2) Why cannot I retrieve directly the SmsResponse object?

You need a JSON provider that can handle deserializing from JSON to SmsResponse. For that you can use the Jackson provider. Hopefully you are using Maven. You can just add this dependency

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey2.version}</version>
</dependency>

You don't need to do anything else. The provider registers itself. If you're not using Maven, let me know, and I'll post all the jars you need. Just let me know what Jersey version you are using.

Aside:

You should understand the different between Content-Type and Accept. The client sends an Accept header when it wants to tell the server what type it wants back. The server may not be able to produce that type, in which case you will get a 406 Not Acceptable.

The Content-Type is used by the client to tell the server what type of data it is sending, such as with POST, but generally not for GET, as GET doesn't send any data. When the server sends back data, it always sets the Content-Type response header to tell the client what type it is getting back.

Your current use of HttpURLConnection is an incorrect usage. Instead of the Content-Type header, you should be setting the Accept header. It works fine because it just sets the wildcard Accept: */*, and Gson doesn't car about Content-Type anyway.


UPDATE

enter image description here

I just created a new Maven project, and added only the above dependency. It has transitive dependencies all all the above. But most already come with the Jersey distribution. Whatever you don't have, that's what you should look for. Mostly Jackson related jars.

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • the status is 200, the body is like the JSON string that I posted before, the headers inside the context of the response is: `{Content-Length=[63], Expires=[Thu, 19 Nov 1981 08:52:00 GMT], Set-Cookie=[PHPSESSID=omntv5nt9ekjlqoh949diaoqp3; path=/], Connection=[close], X-Powered-By=[PHP/5.3.3], Server=[Apache/2.2.15 (CentOS)], Pragma=[no-cache], Cache-Control=[no-store, no-cache, must-revalidate, post-check=0, pre-check=0], Vary=[negotiate], Date=[Mon, 19 Oct 2015 13:28:12 GMT], TCN=[choice], Content-Location=[sendMessage.php], Content-Type=[application/json; charset=UTF-8]}` – Accollativo Oct 19 '15 at 13:34
  • The `Content-Type` is `application/json`. Therefore, I can't think of any reason for `request("application/json")` to result in a 406. – Paul Samsotha Oct 19 '15 at 13:36
  • The body of `invocationBuilder2.get()` is ` 406 Not Acceptable

    Not Acceptable

    An appropriate representation of the requested resource /api/v1.0/sms/sendMessage could not be found on this server.

    Available variants:
    Apache/2.2.15 (CentOS) Server at www.foo.bar Port 443
    `
    – Accollativo Oct 19 '15 at 13:47
  • the headers is: `{Alternates=[{"sendMessage.php" 1 {type text/html}}], Date=[Mon, 19 Oct 2015 13:41:47 GMT], Vary=[negotiate], TCN=[list], Content-Length=[461], Content-Type=[text/html; charset=iso-8859-1], Connection=[close], Server=[Apache/2.2.15 (CentOS)]}` – Accollativo Oct 19 '15 at 13:47
  • You can try two things: `request("application/json; charset=UTF-8")` or `request().accept("application/json")` (which is no different from just doing `request("application/json")`. If neither works, then the server is screwy and can't handle the `Accept` header. Just use the wild card. As long as the server send back the `Content-Type` header as `application/json` and you have the JSON provider, doing `response.readEntity(SmsResponse.class)` should return the `SmsResponse` just fine – Paul Samsotha Oct 19 '15 at 13:51
  • Like I said, the server is screwy. It's a problem with the server, which is not acting appropriately to the HTTP protocol. Just use the wildcard `request()`. You're still getting `Content-Type: application/json` back. That's all you need to be able to deserialize to `SmsResponse` – Paul Samsotha Oct 19 '15 at 14:14
  • I find out the first part of the problem: my uri was missing the **.PHP** after sendMessage!!!! The correct uri is: `https://www.foo.bar/api/v1.0/sms/sendMessage.php?accessKeyId=34rt&idCampaign=4&fromNumber=Test&toNumber=393281234567&message=Inserisci+la+seguente+password%3A+e71a3b` With this the json header is accepted. Thank you, tomorrow I will check your suggest for the second part. – Accollativo Oct 19 '15 at 14:30
  • I downloaded this jar: http://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson/2.22 but I still get the previous error (MessageBodyReader not found) trying to get the SmsResponse object – Accollativo Oct 20 '15 at 08:57
  • That's why I asked if you were using Maven or not. With maven you don't download the jars yourself. The reason is that jars depend on other jars. So maven handles this for you by just adding the Maven dependency in a file called a pom.xml. So if you are not using Maven, then let me know what Jersey version you are using, and I'll list all the jars you need so you can download all of them – Paul Samsotha Oct 20 '15 at 10:20
  • Last version of Jersey, jaxrs-ri-2.22.1. You've right, I've already planned to switch to maven, I just want have a full and fast test of something that work. Do I need other jar? – Accollativo Oct 20 '15 at 13:18