【问题标题】:Debugging NullPointerException in OpenJDK code (inside Sun Classes)在 OpenJDK 代码中调试 NullPointerException(在 Sun 类中)
【发布时间】:2016-07-11 06:50:23
【问题描述】:

在我的一个客户日志中,我看到了 Sun 类的异常。客户端使用 OpenJDK 1.8.0_91。

我试图重现它,但没有任何运气。

从我们的日志来看,我似乎在 JVM 关闭期间(在 ShutdownHook 中)遇到了异常。

问题是该代码在程序的生命周期内工作并按预期发送所有数据,但是在关闭期间,我们有时会收到此 NPE。

关于如何解决的任何想法?我试图查看源代码,但由于某种原因我找不到它。

这是堆栈跟踪:

    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

如果有帮助,我就是我的 HttpClient 实现:

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;
}

}

【问题讨论】:

    标签: java jvm openjdk


    【解决方案1】:

    我的猜测是,在退出前的最终清理期间实际上仍在使用连接时,您会断开与该 URL 的连接(可能是某些终结器执行其工作的结果),它仅不时发生的事实意味着您在没有正确同步的情况下同时使用该 HttpURLConnection。

    检查源here,在此版本的HttpURLConnection.plainConnect0() 中,该行应为1156 而不是1158

    尝试在HttpURLConnection.disconnect() 上设置断点(仅针对该网址?)。

    【讨论】:

    • 感谢来源和提示。我已经附上了我的 HttpClient。关于同步问题,您是否看到了一些让您眼前一亮的东西?对我来说这看起来很安全。
    • writeRequestBodyToOutputStream 中的dataOutputStream 应该连接到它的HttpURLConnection,关闭一个应该关闭另一个。我不知道您的 DoMagic* 类和 JSONClient 中发生了什么,这似乎是一系列事件(对于共享连接对象的非同步使用而言,顺序不确定)有时会导致这种情况。
    • DoMagic 类上的流程基本上只有一个线程,它只将数据推送到一个数组(DoMagic),JsonClient 序列化并使用相关的主体和请求类型创建新的 HttpRequest。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-21
    • 2013-03-23
    • 2012-09-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多