2
在我的一個客戶端日誌中,我看到了Sun類的一個異常。客戶端使用OpenJDK 1.8.0_91。在OpenJDK代碼(內部Sun類)中調試NullPointerException
我試圖重現它沒有任何運氣。
從我們的日誌看來,我似乎在關閉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;
}
}
感謝您的來源和提示。我已附加了我的HttpClient。你看到關於同步問題的事情嗎?對我來說那看起來很安全。 – nadavy
writeRequestBodyToOutputStream中的'dataOutputStream'應該連接到它的'HttpURLConnection',關閉一個應該關閉另一個。我不知道您的DoMagic *類和JSONClient中發生了什麼,它似乎是一系列事件(以非確定性順序來共享連接對象的非同步使用),有時會導致這種情況。 –
基本上,DoMagic類中的流只有一個線程,它只將數據推送到數組(DoMagic),JsonClient使用相關的主體和請求類型序列化並創建新的HttpRequest。 – nadavy