Affects: 5.3.33
We recently had an instance of a remote method call failing but the exception that was caught was a NullPointerException
thrown from ClassUtils.getQualifiedMethodName
via the logging call in RemoteInvocationTraceInterceptor
rather than the original cause.
catch (Throwable ex) {
// ...
else {
if (logger.isInfoEnabled()) {
logger.info("Processing of " + this.exporterNameClause + "remote call resulted in exception: " +
ClassUtils.getQualifiedMethodName(method), ex); // <-- here
}
}
}
I'm not sure what is happening yet to cause method
to be null, but having the original exception would help debug the issue - especially to rule out the possibility that method
being null is a valid state. Presumably given the raised exception wasn't a RuntimeException it wasn't a different NPE thrown from further into the invocation. It also meant that disabling the info level logging would have increased the detail of the exception as the original exception would have been thrown.
Assuming method
being null here is unexpected, could the logging guard against the subsequent NPE?
Comment From: sbrannen
Indeed, org.aopalliance.intercept.MethodInvocation.getMethod()
should never return null
.
Did you encounter the NPE in a testing scenario, potentially with some form of mocks?
Or did you encounter this in a production deployment?
Comment From: tpoliaw
We've seen it a handful of times in production but haven't been able to reproduce it or narrow down what's causing it yet. I'm happy to accept that the bug is probably on our side for that - we have another layer of proxying going on that may be interfering - this was more about the exception being unavailable.
If if helps, the full stack (up to the 'filling in client stack trace' section):
java.lang.IllegalArgumentException: Method must not be null at org.springframework.util.Assert.notNull(Assert.java:201) at org.springframework.util.ClassUtils.getQualifiedMethodName(ClassUtils.java:1066) at org.springframework.util.ClassUtils.getQualifiedMethodName(ClassUtils.java:1053) at org.springframework.remoting.support.RemoteInvocationTraceInterceptor.invoke(RemoteInvocationTraceInterceptor.java:97) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) at jdk.proxy8/jdk.proxy8.$Proxy63.moveTo(Unknown Source) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at org.springframework.remoting.support.RemoteInvocation.invoke(RemoteInvocation.java:215) at org.springframework.remoting.support.DefaultRemoteInvocationExecutor.invoke(DefaultRemoteInvocationExecutor.java:39) at org.springframework.remoting.support.RemoteInvocationBasedExporter.invoke(RemoteInvocationBasedExporter.java:78) at org.springframework.remoting.rmi.RmiBasedExporter.invoke(RmiBasedExporter.java:75) at org.springframework.remoting.rmi.RmiInvocationWrapper.invoke(RmiInvocationWrapper.java:78) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(Unknown Source) at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) at java.rmi/sun.rmi.transport.Transport$1.run(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Unknown Source) at java.rmi/sun.rmi.transport.Transport.serviceCall(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Unknown Source) at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
Comment From: snicoll
@tpoliaw unfortunately RemoteInvocationTraceInterceptor
is deprecated so we have no intention to make further changes to it. Without a reproducer, we can't really help you. Please note that Spring 5.3.x will be EOL shortly and RemoteInvocationTraceInterceptor
has been removed in Spring Framework 6.