2016-07-11 14 views
2

In einem meiner Client-Protokolle habe ich eine Ausnahme von Sun-Klassen gesehen. Der Client verwendet OpenJDK 1.8.0_91.Debuggen von NullPointerException in OpenJDK-Code (innerhalb von Sun-Klassen)

Ich habe versucht, es ohne Glück zu reproduzieren.

Aus unseren Logs scheint es, als würde ich die Ausnahme beim Herunterfahren der JVM (in ShutdownHook) bekommen.

Das Problem ist, dass dieser Code während der Laufzeit des Programms funktioniert und sendet alle Daten wie erwartet, jedoch während des Herunterfahrens von Zeit zu Zeit erhalten wir diese NPE.

Irgendwelche Ideen zur Lösung? Ich habe versucht, den Quellcode zu sehen, aber aus irgendeinem Grund konnte ich ihn nicht finden.

Hier ist der Stack-Trace:

2016-07-08 11:07:58,426 ERROR [Thread-0] [HttpClient] Failed to send 'POST' request to 'https://prod-x-gw.mycompany.co/api/v2/testDoMagic/'. Error: java.lang.NullPointerException 
java.lang.NullPointerException: null 
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1158) ~[na:1.8.0_91] 
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999) ~[na:1.8.0_91] 
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177) ~[na:1.8.0_91] 
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1283) ~[na:1.8.0_91] 
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1258) ~[na:1.8.0_91] 
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250) ~[na:1.8.0_91] 
    at com.my.company.HttpClient.writeRequestBodyToOutputStream(HttpClient.java:152) ~[na:na] 
    at com.my.company.HttpClient.sendRequest(HttpClient.java:52) ~[na:na] 
    at com.my.company.JsonClient.sendHttpRequest(JsonClient.java:187) [na:na] 
    at com.my.company.JsonClient.postRequest(JsonClient.java:92) [na:na] 
    at com.my.company.JsonClient.postRequest(JsonClient.java:86) [na:na] 
    at com.my.company.DoMagicServiceProxy.sendRequest(DoMagicServiceProxy.java:59) [na:na] 
    at com.my.company.DoMagicServiceProxy.submitDoMagic(DoMagicServiceProxy.java:48) [na:na] 
    at com.my.company.DoMagicQueueSender$2.process(DoMagicQueueSender.java:108) [na:na] 
    at com.mycompany..commons.ChunksProcessor.processAsChunks(ChunksProcessor.java:35) [na:na] 
    at com.my.company.DoMagicQueueSender$1.execute(DoMagicQueueSender.java:89) [na:na] 
    at com.my.company.DoMagicQueueSender.shutdown(DoMagicQueueSender.java:46) [na:na] 
    at com.mycompany.DoMagicManager.shutDown(DoMagicManager.java:77) [na:na] 
    at com.mycompany.AM.shutdown(AM.java:145) [na:na] 
    at com.mycompany.AM.access$000(AM.java:17) [na:na] 
    at com.mycompany.AM$1.run(AM.java:157) [na:na] 
2016-07-08 11:07:58,437 ERROR [Thread-0] [DoMagicServiceProxy] Failed while trying to submit DoMagic. Error: 
java.lang.RuntimeException: Failed to send 'POST' request to 'https://prod-x-gw.mycompany.co/api/v2/testDoMagic/'. Error: java.lang.NullPointerException 
    at com.my.company.HttpClient.sendRequest(HttpClient.java:70) ~[na:na] 
    at com.my.company.JsonClient.sendHttpRequest(JsonClient.java:187) ~[na:na] 
    at com.my.company.JsonClient.postRequest(JsonClient.java:92) ~[na:na] 
    at com.my.company.JsonClient.postRequest(JsonClient.java:86) ~[na:na] 
    at com.my.company.DoMagicServiceProxy.sendRequest(DoMagicServiceProxy.java:59) ~[na:na] 
    at com.my.company.DoMagicServiceProxy.submitDoMagic(DoMagicServiceProxy.java:48) ~[na:na] 
    at com.my.company.DoMagicQueueSender$2.process(DoMagicQueueSender.java:108) [na:na] 
    at com.mycompany.commons.ChunksProcessor.processAsChunks(ChunksProcessor.java:35) [na:na] 
    at com.my.company.DoMagicQueueSender$1.execute(DoMagicQueueSender.java:89) [na:na] 
    at com.my.company.DoMagicQueueSender.shutdown(DoMagicQueueSender.java:46) [na:na] 
    at com.mycompany.DoMagicManager.shutDown(DoMagicManager.java:77) [na:na] 
    at com.mycompany.AM.shutdown(AM.java:145) [na:na] 
    at com.mycompany.AM.access$000(AM.java:17) [na:na] 
    at com.mycompany.AM$1.run(AM.java:157) [na:na] 
Caused by: java.lang.NullPointerException: null 
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1158) ~[na:1.8.0_91] 
    at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:999) ~[na:1.8.0_91] 
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:177) ~[na:1.8.0_91] 
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1283) ~[na:1.8.0_91] 
    at sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1258) ~[na:1.8.0_91] 
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.getOutputStream(HttpsURLConnectionImpl.java:250) ~[na:1.8.0_91] 
    at com.my.company.HttpClient.writeRequestBodyToOutputStream(HttpClient.java:152) ~[na:na] 
    at com.my.company.HttpClient.sendRequest(HttpClient.java:52) ~[na:na] 
    ... 13 common frames omitted 

Wenn das hilft, ist, bin ich meine Httpclient Implementierung:

public class HttpClient { 
private int readTimeoutInMS; 
private int connectTimeoutInMS; 
private String charset; 
private final int SECOND = 1000; 
private ILogFactory logFactory; 
private ILogger log; 

public HttpClient(){ 
    //By default, max timeout will be 130 seconds - 10 seconds to connect, and 120 seconds to read the final byte. 
    connectTimeoutInMS = 10 * SECOND; 
    readTimeoutInMS = 120 * SECOND; 
    charset = "utf-8"; 
} 

public void init() { 
    if (log == null) { 
     log = getLogger(); 
    } 
} 

public HttpResponse sendRequest(HttpRequest request) 
{ 
    log.info("sendRequest was called. Remote url:'" + request.getUrl() + "'."); 
    HttpRequestMethods requestMethod = request.getRequestMethod(); 
    HttpURLConnection connection = null; 
    boolean originalFollowRedirects = HttpURLConnection.getFollowRedirects(); 
    try{ 

     URL targetUrl = new URL(request.getUrl()); 
     connection = (HttpURLConnection) targetUrl.openConnection(); 

     setupConnectionObject(request, requestMethod, connection); 

     if (requestMethod == HttpRequestMethods.POST){ 
      writeRequestBodyToOutputStream(request, connection); 
     } 

     HttpResponse response = new HttpResponse(); 
     response.setStatusCode(connection.getResponseCode()); 
     if (response.getStatusCode() >= 400) { 
      response.setResponseStream(connection.getErrorStream()); 
     } 
     else { 
      response.setResponseStream(connection.getInputStream()); 
     } 

     return response; 

    }catch (Exception e) 
    { 
     String msg = String.format("Failed to send '%s' request to '%s'. Error: %s", requestMethod, request.getUrl(), e); 
     log.error(msg,e); 
     throw new RuntimeException(msg, e); 
    } 
    finally { 

     HttpURLConnection.setFollowRedirects(originalFollowRedirects); 
    } 
} 


public int getReadTimeoutInMS() { 
    return readTimeoutInMS; 
} 

public void setReadTimeoutInMS(int readTimeoutInMS) { 
    this.readTimeoutInMS = readTimeoutInMS; 
} 

public int getConnectTimeoutInMS() { 
    return connectTimeoutInMS; 
} 

public void setConnectTimeoutInMS(int connectTimeoutInMS) { 
    this.connectTimeoutInMS = connectTimeoutInMS; 
} 


public String getCharset() { 
    return charset; 
} 

public void setCharset(String charset) { 
    this.charset = charset; 
} 

public ILogFactory getLogFactory() { 
    return logFactory; 
} 

public void setLogFactory(ILogFactory logFactory) { 
    this.logFactory = logFactory; 
} 

private void setupConnectionObject(HttpRequest request, HttpRequestMethods requestMethod, HttpURLConnection connection)throws ProtocolException { 
    connection.setRequestMethod(requestMethod.toString()); 

    if (requestMethod == HttpRequestMethods.POST){ 
     //Mark the HttpMethod as post. 
     connection.setDoOutput(true); // Allows to send message body on the output stream. 
    } else if (requestMethod == HttpRequestMethods.HEAD) 
    { 
     HttpURLConnection.setFollowRedirects(false); 
     connection.setRequestMethod("HEAD"); 
    } 

    int connectTimeout = request.getConnectTimeout() != null? request.getConnectTimeout() : this.connectTimeoutInMS; 
    if (connectTimeout >= 0) 
    { 
     connection.setConnectTimeout(connectTimeout); 
    } 

    int readTimeout = request.getReadTimeout() != null? request.getReadTimeout() : this.readTimeoutInMS; 
    if (connectTimeout >= 0) 
    { 
     connection.setReadTimeout(readTimeout); 
    } 

    //Add HttpHeader 
    if (request.getHttpHeaders() != null) 
    { 
     for (Entry<String, String> httpHeader : request.getHttpHeaders().entrySet()) { 
      connection.setRequestProperty(httpHeader.getKey(), httpHeader.getValue()); 
     } 
    } 
    if (request.getCompressRequestBody() && connection.getDoOutput()){ //only turn on for request with output 
     connection.setRequestProperty("Content-Encoding","gzip"); 
    } 
} 
private void writeRequestBodyToOutputStream(HttpRequest request, 
     HttpURLConnection connection) throws IOException, 
     UnsupportedEncodingException { 
    DataOutputStream dataOutputSteam = null; 
    try{ 
     dataOutputSteam = new DataOutputStream(connection.getOutputStream()); 
     String requestBody = request.getRequestBody() != null ? request.getRequestBody() : ""; 
     String bodyCharset = request.getCharset() != null ? request.getCharset() : this.charset; 
     byte[] requestBodyAsBytes = requestBody.getBytes(bodyCharset); 

     if (request.getCompressRequestBody()) { 
      //http://stackoverflow.com/questions/7153484/gzip-post-request-with-httpclient-in-java 
      ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
      try (GZIPOutputStream gzos = new GZIPOutputStream(baos)) { 
       gzos.write(requestBodyAsBytes); 
      } 

      byte[] gzippedBytes = baos.toByteArray(); 
      requestBodyAsBytes = gzippedBytes; 
     } 
     dataOutputSteam.write(requestBodyAsBytes); 
    } 
    finally{ 
     if (dataOutputSteam != null) 
     { 
      dataOutputSteam.close(); 
     } 
    } 
} 

private ILogger getLogger() { 
    ILogger logger; 
    if (logFactory != null) 
    { 
     logger = logFactory.getLogger("HttpClient"); 
    } 
    else 
    { 
     logger = NullLogger.INSTANCE; 
    } 
    return logger; 
} 

}

Antwort

2

Meine Vermutung ist, dass Sie von dieser URL zu trennen, wenn die Verbindung wird tatsächlich noch während der letzten Bereinigung vor dem Beenden verwendet (vielleicht ist es das Ergebnis eines Finalizers, der seine Aufgabe erledigt), die Tatsache, dass es nur von Zeit zu Zeit passiert, impliziert t Sie verwenden diese HttpURLConnection gleichzeitig ohne ordnungsgemäße Synchronisierung.

Überprüfen Sie die Quelle here, in dieser Version von HttpURLConnection.plainConnect0() diese Linie sollte 1156 statt 1158 sein.

Versuchen Sie einen Haltepunkt auf HttpURLConnection.disconnect() zu setzen (nur für diese URL?).

+0

Danke für die Quelle und Tipps. Ich habe meinen HttpClient angehängt. Siehst du etwas, das deine Aufmerksamkeit in Bezug auf das Synchronisierungsproblem erregt? Für mich sieht das sicher aus. – nadavy

+0

Der 'dataOutputStream' in' writeRequestBodyToOutputStream' sollte mit seiner 'HttpURLConnection' verbunden sein, beim Schließen sollte man den anderen schließen. Ich weiß nicht, was in Ihren DoMagic * -Klassen passiert und in JSONClient scheint es eine Folge von Ereignissen (in nichtdeterministischer Reihenfolge für die unsynchronisierte Verwendung des gemeinsamen Verbindungsobjekts) zu geben, die manchmal dazu führen. –

+0

Grundsätzlich hat der Ablauf der DoMagic-Klassen einen einzelnen Thread, der nur Daten in ein Array (DoMagic) und den JsonClient serialisiert und einen neuen HttpRequest mit dem entsprechenden Body- und Anforderungstyp erstellt. – nadavy

Verwandte Themen