1

In my REST application I use this class to send requests:

public final class Utility {

private static Client client = ClientBuilder.newClient();

private Utility(){
}

public static <T> Response sendRequest(String URI, String method, T payload){
    Response response = null;
    WebTarget target = client.target(URI);
    switch(method){
    case "GET": 
        response = target.request().header("Content-type", "application/json").get();
        break;
    case "POST":
        response = target.request().header("Content-type", "application/json").post(Entity.json(payload));
        break;
    case "PUT":
        response = target.request().header("Content-type", "application/json").put(Entity.json(payload));
        break;
    case "DELETE":
        response = target.request().header("Content-type", "application/json").delete();
        break;
    default:
        throw new NotAllowedException("Invalid method type was given "
                + "to the Utility.sendRequest() method");
    }

    if(response == null){
        throw new UnavailableServerException("The server at (" + URI + ") did not respond.");

    }

    return response;
}
}

I have recently had the idea that I want to handle cases when I send a request to an endpoint, which cant respond. (I didn't start the grizzly http server for it). I learned that if I try to send a request to an endpoint like this, my general ExceptionMapper (which implements ExceptionMapper(Exception)) catches it, and the exception was a NullPointerException (NPE).

So I figured all I have to do is just outsource the Response to a variable, and check if it's == null before returning it. With printfs and debug mode, I saw the following behaviour:

  • Even for the failed request, response == null is not true.
  • The failed request does get to the return response line, but the very next line in my resource method is not executed, exception happens.
  • I tried to catch the NPE (even with catch(Exception e)) with try-catch blocks inside the Utility class, and inside the resource method too, with no succes.

I've tried to find out what throws NPE in the javax.ws.rs.core.Response; documentation -> no success. I'm out of ideas :/ (Btw UnavailableServerException is my custom exception also.)

Also what's the proper way to check is the request was successful, since the null check obviously not a good solution.

EDIT: here is the stacktrace

javax.ws.rs.ProcessingException: Already connected
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:264)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:684)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:681)
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:681)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:437)
at org.glassfish.jersey.client.JerseyInvocation$Builder.put(JerseyInvocation.java:326)
at eu.arrowhead.common.Utility.sendRequest(Utility.java:32)
at eu.arrowhead.core.gatekeeper.GatekeeperResource.GSDPoll(GatekeeperResource.java:146)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
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:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384)
at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:224)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalStateException: Already connected
at sun.net.www.protocol.http.HttpURLConnection.setRequestProperty(Unknown Source)
at org.glassfish.jersey.client.internal.HttpUrlConnector.setOutboundHeaders(HttpUrlConnector.java:421)
at org.glassfish.jersey.client.internal.HttpUrlConnector.access$100(HttpUrlConnector.java:96)
at org.glassfish.jersey.client.internal.HttpUrlConnector$4.getOutputStream(HttpUrlConnector.java:384)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:200)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commitStream(CommittingOutputStream.java:194)
at org.glassfish.jersey.message.internal.CommittingOutputStream.commit(CommittingOutputStream.java:262)
at org.glassfish.jersey.message.internal.OutboundMessageContext.commitStream(OutboundMessageContext.java:816)
at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:545)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:388)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:285)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:255)
... 37 more

EDIT2:

@Provider
public class GenericExceptionMapper implements ExceptionMapper<Exception> {

@Override
public Response toResponse(Exception ex) {
    ErrorMessage errorMessage = new ErrorMessage("Class: " + ex.getClass().toString() + 
            " Message: " + ex.getMessage(), 500, "No documentation yet.");
    return Response.status(Status.INTERNAL_SERVER_ERROR)
            .entity(errorMessage)
            .build();
}

}

Here is my generic exception mapper too, although I do think this is not part of the problem, rather than the fact that that in my example my request involves 3 servers. This request is sent to an online server, which sends something to a 2nd online server (using the same utility class method), which polls a 3rd, which is offline in my test. The 2nd server cathes the ProcessingException (which far as I know can be the okay response if the 3rd server is offline) and sends back my custom exception. but then at the 1st server somehow cant send this same exception back to my Postman client, and a generic NPE is thrown.

I'm gonna try to find the reason for that tomorrow though. I'll also update jersey from 2.22.1 to 2.23.1.

logi0517
  • 813
  • 1
  • 13
  • 32
  • try to put your code inside the `try catch` block. I think when server is not available, HttpException is thrown. – Valijon Jul 26 '16 at 13:21
  • Please show the actual stacktrace – OneCricketeer Jul 26 '16 at 13:22
  • I cant really show a stacktrace sadly. Even when I unregister my generic exception mapper, the only thing I get back in postman is the Grizzly html page, I think something there masks the stacktrace. @Valijon I have already tried this, like I mentioned in the post, no idea why doesnt it work. – logi0517 Jul 26 '16 at 13:25
  • Can you show us your `ExceptionMapper`? Maybe the problem is there. As you see, exception is identified (`ProcessingException`), maybe your `ExceptionMapper` is not configured correctly and throws you NPE. – Valijon Jul 26 '16 at 15:28
  • added it in the 2nd edit – logi0517 Jul 26 '16 at 17:51

2 Answers2

1

The response shouldn't be null. Just like visiting a page through the browser when it doesn't exist, you should get some server error. Check the response.getStatus() for something in the 400's

You might also try putting in some sensible timeouts.

public static <T> Response sendRequest(String URI, String method, T payload){
try{
    ClientConfig configuration = new ClientConfig();
    configuration.property(ClientProperties.CONNECT_TIMEOUT, 10000);
    configuration.property(ClientProperties.READ_TIMEOUT, 10000);
    Client client = ClientBuilder.newClient(configuration);

    Response response = null;
    WebTarget target = client.target(UriBuilder.fromUri(URI).build())
    switch(method){
    case "GET": 
        response = target.request().header("Content-type", "application/json").get();
        break;
    case "POST":
        response = target.request().header("Content-type", "application/json").post(Entity.json(payload));
        break;
    case "PUT":
        response = target.request().header("Content-type", "application/json").put(Entity.json(payload));
        break;
    case "DELETE":
        response = target.request().header("Content-type", "application/json").delete();
        break;
    default:
        throw new NotAllowedException("Invalid method type was given "
                + "to the Utility.sendRequest() method");
    }

    if(response.getStatus() != 200){  //200 OK!
        throw new UnavailableServerException("The server at (" + URI + ") did not respond.");
    }
    return response;
}catch(Exception e){
    e.printStackTrace();
}
return Response.status(Status.NOT_FOUND).build();
}
Mark D
  • 233
  • 3
  • 17
-1

I've put try catch block and got ProcessingException

ScopedJaxrsResponse{ClientResponse{method=GET, uri=http://google.com/, status=200, reason=OK}}
javax.ws.rs.ProcessingException: java.net.UnknownHostException: foo.bar
    at org.glassfish.jersey.client.HttpUrlConnector.apply(HttpUrlConnector.java:205)
    at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:217)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:655)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:652)
    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:422)
    at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:652)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:387)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:291)
    at foo.Utility.sendRequest(Utility.java:32)
    at foo.Utility.a(Utility.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.net.UnknownHostException: foo.bar
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:178)
    at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:579)
    at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:432)
    at sun.net.www.http.HttpClient.openServer(HttpClient.java:527)
    at sun.net.www.http.HttpClient.<init>(HttpClient.java:211)
    at sun.net.www.http.HttpClient.New(HttpClient.java:308)
    at sun.net.www.http.HttpClient.New(HttpClient.java:326)
    at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:997)
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:933)
    at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:851)
    at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1301)
    at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:468)
    at org.glassfish.jersey.client.HttpUrlConnector._apply(HttpUrlConnector.java:296)
    at org.glassfish.jersey.client.HttpUrlConnector.apply(HttpUrlConnector.java:203)
    ... 35 more

Junit error:

java.lang.IllegalArgumentException: The server at (http://foo.bar/) did not respond.
    at foo.Utility.sendRequest(Utility.java:53)
    at foo.Utility.a(Utility.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Code:

import javax.ws.rs.NotAllowedException;
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.Response;

import org.junit.Test;

public final class Utility {

    private static Client client = ClientBuilder.newClient();

    public Utility() {

    }

    @Test
    public void a() {
        System.out.println(Utility.sendRequest("http://google.com/", "GET", ""));
        System.out.println(Utility.sendRequest("http://foo.bar/", "GET", ""));
    }

    public static <T> Response sendRequest(String URI, String method, T payload) {
        Response response = null;
        try {
            WebTarget target = client.target(URI);
            switch (method) {
            case "GET":
                response = target.request().header("Content-type", "application/json").get();
                break;
            case "POST":
                response = target.request().header("Content-type", "application/json").post(Entity.json(payload));
                break;
            case "PUT":
                response = target.request().header("Content-type", "application/json").put(Entity.json(payload));
                break;
            case "DELETE":
                response = target.request().header("Content-type", "application/json").delete();
                break;
            default:
                throw new NotAllowedException("Invalid method type was given " + "to the Utility.sendRequest() method");
            }
        } catch(Exception e) {
            e.printStackTrace();
            response = null;
        }

        if (response == null) {
            throw new IllegalArgumentException("The server at (" + URI + ") did not respond.");

        }

        return response;
    }
}
Valijon
  • 12,667
  • 4
  • 34
  • 67
  • i forgot i can get the stacktrace like this. see my edit – logi0517 Jul 26 '16 at 13:52
  • btw in Postman it still shows a frikin NPE, when I have my try-catch block and the if statement after it set up, just like yours. how is that possible? – logi0517 Jul 26 '16 at 13:56
  • what is your Jax-RS version? – Valijon Jul 26 '16 at 14:11
  • My setup was: request -> receiver sends another request to the offline server. I checked, my custom exception is thrown at the receiver, but it does not get back to the original requester yet. But I think i can figure it out how to get around that. (jersey version 2.22.1 btw) – logi0517 Jul 26 '16 at 14:11
  • create new client each request and you will not get this error. Seems this fixed error: [https://java.net/jira/browse/JERSEY-2728](https://java.net/jira/browse/JERSEY-2728) – Valijon Jul 26 '16 at 14:15
  • I moved the client declaration inside the try block, didn't change the stacktrace. – logi0517 Jul 26 '16 at 14:18