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