Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
package org.tron.core.services.jsonrpc;

import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.jsonrpc4j.ErrorData;
import com.googlecode.jsonrpc4j.ErrorResolver;
import com.googlecode.jsonrpc4j.JsonRpcError;
import com.googlecode.jsonrpc4j.JsonRpcErrors;
import com.googlecode.jsonrpc4j.ProxyUtil;
import com.googlecode.jsonrpc4j.ReflectionUtil;
import java.lang.reflect.Method;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.tron.core.exception.TronError;
import org.tron.core.exception.jsonrpc.JsonRpcException;

/**
* {@link ErrorResolver} that uses annotations.
*/
@Slf4j(topic = "API")
public enum JsonRpcErrorResolver implements ErrorResolver {
INSTANCE;

Expand All @@ -22,29 +26,108 @@ public enum JsonRpcErrorResolver implements ErrorResolver {
@Override
public JsonError resolveError(
Throwable thrownException, Method method, List<JsonNode> arguments) {
JsonRpcError resolver = getResolverForException(thrownException, method);
if (notFoundResolver(resolver)) {
return null;
Error fatal = findFatalCause(thrownException);
if (fatal != null) {
throw fatal;
}

JsonRpcError resolver = method == null
? null : getResolverForException(thrownException, method);
if (resolver == null) {
logUnhandledException(method, thrownException);
return new JsonError(JsonError.INTERNAL_ERROR.code, "Internal error", null);
}

String message = hasErrorMessage(resolver) ? resolver.message() : thrownException.getMessage();
if (StringUtils.isBlank(message)) {
message = defaultMessageFor(resolver.code());
}

// data priority: exception > annotation > default ErrorData
// data priority: exception > annotation
Object data = null;
if (thrownException instanceof JsonRpcException) {
JsonRpcException jsonRpcException = (JsonRpcException) thrownException;
data = jsonRpcException.getData();
}

if (data == null) {
data = hasErrorData(resolver)
? resolver.data()
: new ErrorData(resolver.exception().getName(), message);
if (data == null && hasErrorData(resolver)) {
data = resolver.data();
}

return new JsonError(resolver.code(), message, data);
}

/**
* Returns the first {@link VirtualMachineError}, {@link ThreadDeath}, {@link LinkageError} or
* {@link TronError} found in {@code throwable} or its cause chain, or {@code null} if there is
* none.
*
* <p>This is the JSON-RPC layer's propagation policy for fatal causes, not a statement that
* other {@link Error}s are safe to recover from. Callers should apply it before any logging so a
* fatal cause is never recorded as an ordinary failure. The scan has no depth cutoff, terminates
* on cyclic cause chains, and uses neither recursion nor auxiliary collections.
*/
public static Error findFatalCause(Throwable throwable) {
// Avoid allocations when handling memory exhaustion, without truncating deep cause chains.
// Check both fast-pointer steps so cycle detection cannot skip a fatal cause inside a cycle.
Throwable slow = throwable;
Throwable fast = throwable;
while (fast != null) {
if (isFatal(fast)) {
return (Error) fast;
}
fast = fast.getCause();
if (fast == null) {
return null;
}
if (isFatal(fast)) {
return (Error) fast;
}
fast = fast.getCause();
slow = slow.getCause();
if (fast == slow) {
return null;
}
}
return null;
}

private static boolean isFatal(Throwable cause) {
return cause instanceof VirtualMachineError
|| cause instanceof ThreadDeath
|| cause instanceof LinkageError
|| cause instanceof TronError;
}

private static void logUnhandledException(Method method, Throwable thrownException) {
logger.debug("Unhandled exception in JSON-RPC method {}", rpcMethodName(method),
thrownException);
}

private static String rpcMethodName(Method method) {
if (method == null) {
return "unknown";
}
try {
return ProxyUtil.getMethodName(method);
} catch (RuntimeException e) {
return method.getName();
}
}

private static String defaultMessageFor(int code) {
switch (code) {
case -32600:
return "Invalid Request";
case -32601:
return "Method not found";
case -32602:
return "Invalid params";
default:
return "Internal error";
}
}

private JsonRpcError getResolverForException(Throwable thrownException, Method method) {
JsonRpcErrors errors = ReflectionUtil.getAnnotation(method, JsonRpcErrors.class);
if (hasAnnotations(errors)) {
Expand All @@ -57,10 +140,6 @@ private JsonRpcError getResolverForException(Throwable thrownException, Method m
return null;
}

private boolean notFoundResolver(JsonRpcError resolver) {
return resolver == null;
}

private boolean hasErrorMessage(JsonRpcError em) {
// noinspection ConstantConditions
return em.message() != null && !em.message().trim().isEmpty();
Expand All @@ -78,4 +157,4 @@ private boolean hasAnnotations(JsonRpcErrors errors) {
private boolean isExceptionInstanceOfError(Throwable target, JsonRpcError em) {
return em.exception().isInstance(target);
}
}
}
Loading
Loading