From 77455111a171c961be09759416673012dcb949b6 Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 21 Nov 2024 09:15:39 +0530 Subject: [PATCH 1/2] Handle bal future panic by logging the error --- .../ballerina/stdlib/http/api/DataContext.java | 18 ++++++++++++------ .../stdlib/http/api/HttpConstants.java | 2 ++ .../http/api/client/actions/HasPromise.java | 9 ++++++++- .../api/client/actions/HttpClientAction.java | 15 +++++++++++++-- .../ExternHttpDataSourceBuilder.java | 9 ++++++++- .../api/nativeimpl/connection/Respond.java | 12 ++++++------ 6 files changed, 49 insertions(+), 16 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java b/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java index 768fca6ec0..c9a2b7d23d 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java @@ -25,6 +25,8 @@ import io.ballerina.stdlib.http.transport.contract.HttpClientConnector; import io.ballerina.stdlib.http.transport.message.HttpCarbonMessage; +import static io.ballerina.stdlib.http.api.HttpConstants.FUTURE_COMPLETE_ERR_MSG; + /** * {@code DataContext} is the wrapper to hold {@code Context} and {@code Callback}. */ @@ -55,17 +57,17 @@ public DataContext(Environment environment, HttpCarbonMessage inboundRequestMsg) public void notifyInboundResponseStatus(BObject inboundResponse, BError httpConnectorError) { //Make the request associate with this response consumable again so that it can be reused. if (inboundResponse != null) { - getFuture().complete(inboundResponse); + completeFuture(inboundResponse, "notify inbound response status"); } else if (httpConnectorError != null) { - getFuture().complete(httpConnectorError); + completeFuture(httpConnectorError, "notify inbound response connector error"); } else { BError err = HttpUtil.createHttpError("inbound response retrieval error", HttpErrorType.CLIENT_ERROR); - getFuture().complete(err); + completeFuture(err, "notify inbound response retrieval error"); } } public void notifyOutboundResponseStatus(BError httpConnectorError) { - getFuture().complete(httpConnectorError); + completeFuture(httpConnectorError, "notify outbound response connector error"); } public HttpCarbonMessage getOutboundRequest() { @@ -84,7 +86,11 @@ public Environment getEnvironment() { return environment; } - public Future getFuture() { - return balFuture; + public void completeFuture(Object result, String methodCall) { + try { + balFuture.complete(result); + } catch (BError err) { + System.err.printf(FUTURE_COMPLETE_ERR_MSG, methodCall, err.getMessage()); + } } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java index 7ee9c3d5d3..c08cf26928 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpConstants.java @@ -613,6 +613,8 @@ public class HttpConstants { public static final BString REQUEST_CTX_MEMBERS = StringUtils.fromString("members"); + public static final String FUTURE_COMPLETE_ERR_MSG = "%s failed with bal future completion error: %s%n"; + private HttpConstants() { } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HasPromise.java b/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HasPromise.java index c100b351fb..4a5a8e8786 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HasPromise.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HasPromise.java @@ -18,6 +18,7 @@ import io.ballerina.runtime.api.Environment; import io.ballerina.runtime.api.Future; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; import io.ballerina.stdlib.http.api.HttpConstants; import io.ballerina.stdlib.http.api.HttpUtil; @@ -25,6 +26,8 @@ import io.ballerina.stdlib.http.transport.contract.HttpClientConnectorListener; import io.ballerina.stdlib.http.transport.message.ResponseHandle; +import static io.ballerina.stdlib.http.api.HttpConstants.FUTURE_COMPLETE_ERR_MSG; + /** * {@code HasPromise} action can be used to check whether a push promise is available. */ @@ -51,7 +54,11 @@ private static class PromiseAvailabilityCheckListener implements HttpClientConne @Override public void onPushPromiseAvailability(boolean isPromiseAvailable) { - balFuture.complete(isPromiseAvailable); + try { + balFuture.complete(isPromiseAvailable); + } catch (BError err) { + System.err.printf(FUTURE_COMPLETE_ERR_MSG, "check promise availability", err.getMessage()); + } } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java b/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java index 405c062e68..a5e2b19280 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java @@ -52,6 +52,7 @@ import static io.ballerina.stdlib.http.api.HttpConstants.CURRENT_TRANSACTION_CONTEXT_PROPERTY; import static io.ballerina.stdlib.http.api.HttpConstants.EMPTY; import static io.ballerina.stdlib.http.api.HttpConstants.EQUAL_SIGN; +import static io.ballerina.stdlib.http.api.HttpConstants.FUTURE_COMPLETE_ERR_MSG; import static io.ballerina.stdlib.http.api.HttpConstants.MAIN_STRAND; import static io.ballerina.stdlib.http.api.HttpConstants.ORIGIN_HOST; import static io.ballerina.stdlib.http.api.HttpConstants.POOLED_BYTE_BUFFER_FACTORY; @@ -226,7 +227,12 @@ private static Object invokeClientMethod(Environment env, BObject client, String env.getRuntime().invokeMethodAsync(client, methodName, null, null, new Callback() { @Override public void notifySuccess(Object result) { - balFuture.complete(result); + try { + balFuture.complete(result); + } catch (BError err) { + System.err.printf(FUTURE_COMPLETE_ERR_MSG, "invoke client method with success result", + err.getMessage()); + } } @Override @@ -234,7 +240,12 @@ public void notifyFailure(BError bError) { BError invocationError = HttpUtil.createHttpError("client method invocation failed: " + bError.getErrorMessage(), HttpErrorType.CLIENT_ERROR, bError); - balFuture.complete(invocationError); + try { + balFuture.complete(invocationError); + } catch (BError err) { + System.err.printf(FUTURE_COMPLETE_ERR_MSG, "invoke client method with failure result", + err.getMessage()); + } } }, propertyMap, PredefinedTypes.TYPE_NULL, paramFeed); return null; diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java index fc9498b0b4..eb8ae48578 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java @@ -40,6 +40,7 @@ import java.util.Locale; import java.util.Objects; +import static io.ballerina.stdlib.http.api.HttpConstants.FUTURE_COMPLETE_ERR_MSG; import static io.ballerina.stdlib.mime.util.EntityBodyHandler.constructBlobDataSource; import static io.ballerina.stdlib.mime.util.EntityBodyHandler.constructJsonDataSource; import static io.ballerina.stdlib.mime.util.EntityBodyHandler.constructStringDataSource; @@ -229,7 +230,13 @@ private static void createErrorAndNotify(Future balFuture, String errMsg) { } private static void setReturnValuesAndNotify(Future balFuture, Object result) { - balFuture.complete(result); + try { + balFuture.complete(result); + } catch (BError error) { + String resultType = result instanceof BError ? "error" : "success"; + System.err.printf(FUTURE_COMPLETE_ERR_MSG, "return data source builder " + resultType + " result", + error.getMessage()); + } } private static void updateDataSourceAndNotify(Future balFuture, BObject entityObj, diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java index 03242c8c95..9ad07d0bdd 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/connection/Respond.java @@ -92,7 +92,7 @@ public static Object nativeRespondWithDataCtx(Environment env, BObject connectio "as the response has been already used.", inboundRequestMsg.getSequenceId()); } BError httpError = HttpUtil.createHttpError(errorMessage, HttpErrorType.GENERIC_LISTENER_ERROR); - dataContext.getFuture().complete(httpError); + dataContext.completeFuture(httpError, "notify outbound dirty response"); return null; } outboundResponseObj.addNativeData(HttpConstants.DIRTY_RESPONSE, true); @@ -107,7 +107,7 @@ public static Object nativeRespondWithDataCtx(Environment env, BObject connectio HttpUtil.checkFunctionValidity(inboundRequestMsg, outboundResponseMsg); } catch (BError e) { log.debug(e.getPrintableStackTrace(), e); - dataContext.getFuture().complete(e); + dataContext.completeFuture(e, "notify function availability failure"); return null; } @@ -151,14 +151,14 @@ public static Object nativeRespondWithDataCtx(Environment env, BObject connectio } } catch (BError e) { log.debug(e.getPrintableStackTrace(), e); - dataContext.getFuture().complete( - HttpUtil.createHttpError(e.getMessage(), HttpErrorType.GENERIC_LISTENER_ERROR)); + dataContext.completeFuture(HttpUtil.createHttpError(e.getMessage(), HttpErrorType.GENERIC_LISTENER_ERROR), + "notify outbound response sent error"); } catch (Throwable e) { //Exception is already notified by http transport. String errorMessage = "Couldn't complete outbound response: " + e.getMessage(); log.debug(errorMessage, e); - dataContext.getFuture().complete( - HttpUtil.createHttpError(errorMessage, HttpErrorType.GENERIC_LISTENER_ERROR)); + dataContext.completeFuture(HttpUtil.createHttpError(errorMessage, HttpErrorType.GENERIC_LISTENER_ERROR), + "notify outbound response completion error"); } return null; } From fa4d726020f630c1e5d9a1f9fa205a8773294f6f Mon Sep 17 00:00:00 2001 From: TharmiganK Date: Thu, 21 Nov 2024 09:37:37 +0530 Subject: [PATCH 2/2] Print error stacktrace --- .../java/io/ballerina/stdlib/http/api/DataContext.java | 1 + .../stdlib/http/api/HttpCallableUnitCallback.java | 10 ++-------- .../http/api/HttpRequestInterceptorUnitCallback.java | 8 +------- .../http/api/HttpResponseInterceptorUnitCallback.java | 8 +------- .../java/io/ballerina/stdlib/http/api/HttpUtil.java | 6 ++++++ .../http/api/client/actions/HttpClientAction.java | 2 ++ .../api/nativeimpl/ExternHttpDataSourceBuilder.java | 1 + 7 files changed, 14 insertions(+), 22 deletions(-) diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java b/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java index c9a2b7d23d..da4325f878 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/DataContext.java @@ -91,6 +91,7 @@ public void completeFuture(Object result, String methodCall) { balFuture.complete(result); } catch (BError err) { System.err.printf(FUTURE_COMPLETE_ERR_MSG, methodCall, err.getMessage()); + HttpUtil.printStacktraceIfError(result); } } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java index 09a58b2be9..77a38d9898 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpCallableUnitCallback.java @@ -130,7 +130,7 @@ public void invokeBalMethod(Object[] paramFeed, String methodName) { @Override public void notifySuccess(Object result) { stopObserverContext(); - printStacktraceIfError(result); + HttpUtil.printStacktraceIfError(result); } @Override @@ -188,17 +188,11 @@ private boolean alreadyResponded(Object result) { HttpUtil.methodInvocationCheck(requestMessage, HttpConstants.INVALID_STATUS_CODE, ILLEGAL_FUNCTION_INVOKED); } catch (BError e) { if (result != null) { // handles nil return and end of resource exec - printStacktraceIfError(result); + HttpUtil.printStacktraceIfError(result); err.println(HttpConstants.HTTP_RUNTIME_WARNING_PREFIX + e.getMessage()); } return true; } return false; } - - private void printStacktraceIfError(Object result) { - if (result instanceof BError) { - ((BError) result).printStackTrace(); - } - } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java index 76cc1302c3..3725d9e7ff 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpRequestInterceptorUnitCallback.java @@ -101,12 +101,6 @@ private boolean alreadyResponded() { return false; } - private void printStacktraceIfError(Object result) { - if (result instanceof BError) { - ((BError) result).printStackTrace(); - } - } - private void sendRequestToNextService() { ballerinaHTTPConnectorListener.onMessage(requestMessage); } @@ -186,7 +180,7 @@ public void invokeBalMethod(Object[] paramFeed, String methodName) { Callback returnCallback = new Callback() { @Override public void notifySuccess(Object result) { - printStacktraceIfError(result); + HttpUtil.printStacktraceIfError(result); } @Override diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java index 00f5d223c2..05e20f95fe 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpResponseInterceptorUnitCallback.java @@ -88,12 +88,6 @@ public void invokeErrorInterceptors(BError error, boolean printError) { returnErrorResponse(error); } - private void printStacktraceIfError(Object result) { - if (result instanceof BError) { - ((BError) result).printStackTrace(); - } - } - private void sendResponseToNextService() { Respond.nativeRespondWithDataCtx(environment, caller, response, dataContext); } @@ -171,7 +165,7 @@ public void invokeBalMethod(Object[] paramFeed, String methodName) { public void notifySuccess(Object result) { stopObserverContext(); dataContext.notifyOutboundResponseStatus(null); - printStacktraceIfError(result); + HttpUtil.printStacktraceIfError(result); } @Override diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java index 8cf4716653..71f0c874ce 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/HttpUtil.java @@ -1971,6 +1971,12 @@ public static boolean isHttpStatusCodeResponseTypeWithBody(Type type) { return false; } + public static void printStacktraceIfError(Object result) { + if (result instanceof BError) { + ((BError) result).printStackTrace(); + } + } + private HttpUtil() { } } diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java b/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java index a5e2b19280..8dd400200b 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/client/actions/HttpClientAction.java @@ -232,6 +232,7 @@ public void notifySuccess(Object result) { } catch (BError err) { System.err.printf(FUTURE_COMPLETE_ERR_MSG, "invoke client method with success result", err.getMessage()); + HttpUtil.printStacktraceIfError(result); } } @@ -245,6 +246,7 @@ public void notifyFailure(BError bError) { } catch (BError err) { System.err.printf(FUTURE_COMPLETE_ERR_MSG, "invoke client method with failure result", err.getMessage()); + HttpUtil.printStacktraceIfError(invocationError); } } }, propertyMap, PredefinedTypes.TYPE_NULL, paramFeed); diff --git a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java index eb8ae48578..a9212f0cea 100644 --- a/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java +++ b/native/src/main/java/io/ballerina/stdlib/http/api/nativeimpl/ExternHttpDataSourceBuilder.java @@ -236,6 +236,7 @@ private static void setReturnValuesAndNotify(Future balFuture, Object result) { String resultType = result instanceof BError ? "error" : "success"; System.err.printf(FUTURE_COMPLETE_ERR_MSG, "return data source builder " + resultType + " result", error.getMessage()); + HttpUtil.printStacktraceIfError(result); } }