Bug: Direct class cast causes ClassCastException in TestRPC#testReaderExceptions
Status: Open | Priority: Critical | Resolution: 
Created: 20/Jul/23 07:19

h2. What happened:

Test {{TestRPC#testReaderExceptions}} assumes there is a RemoteException has been thrown and directly cast the root cause of {{ServiceException}} to {{{}RemoteException{}}}. But this cast may lead to ClassCastException when the root cause exception is not {{{}RemoteException{}}}.
h2. Buggy code:
{code:java}
@Test (timeout=30000)
public void testReaderExceptions() throws Exception {
      ...
      try {
        FakeRequestClass.exception = doDisconnect ? rseFatal : rseError;
        proxy.ping(null, newEmptyRequest());
        fail(reqName + " didn't fail");
      } catch (ServiceException e) {
        RemoteException re = (RemoteException)e.getCause();       // <--- Here the test assumes the root cause is RemoteException
        assertEquals(reqName, expectedIOE, re.unwrapRemoteException());
      } {code}
h2. How to reproduce:

(1) Set {{hadoop.security.authentication}} to {{kerberos}}
(2) Run test {{TestRPC#testReaderExceptions}}
h2. Stack trace:
{code:java}
java.lang.ClassCastException: class java.io.IOException cannot be cast to class org.apache.hadoop.ipc.RemoteException (java.io.IOException is in module java.base of loader 'bootstrap'; org.apache.had
oop.ipc.RemoteException is in unnamed module of loader 'app')                  
        at org.apache.hadoop.ipc.TestRPC.testReaderExceptions(TestRPC.java:1742)                                                                                                                       
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:566)                              
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)     
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:299)
        at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:293)                                                                                          
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)                      
        at java.base/java.lang.Thread.run(Thread.java:829)   {code}
h2. Fix:

A better way to do is to firstly check whether the root cause is RemoteException, if not, fail the test rather than causing the CastClassException:


{code:java}
  try {
    FakeRequestClass.exception = doDisconnect ? rseFatal : rseError;
    proxy.ping(null, newEmptyRequest());
    fail(reqName + " didn't fail");
  } catch (ServiceException e) {
    if (e.getCause() instanceof RemoteException) {
      RemoteException re = (RemoteException)e.getCause();
      assertEquals(reqName, expectedIOE, re.unwrapRemoteException());
    } else {
      fail(reqName + " didn't fail with RemoteException");
    }
  } {code}