Uploaded image for project: 'Infinispan'
  1. Infinispan
  2. ISPN-12866

Generic exception externalizer can throw CNFEs when unmarshalling exception response

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Critical Critical
    • None
    • 11.0.9.Final
    • Marshalling
    • None
    • Undefined

      If a remote cache operation returns exceptionally, the chain of exception causes can easily contain an exception class not known to Infinispan's classloader.

      This causes a ClassNotFoundException during unmarshalling of the ExceptionResponse object, which obscure the root cause of the cache operation failure.

      e.g.

      2021-02-17 17:02:27,340 ERROR [org.infinispan.CLUSTER] (thread-14,ejb,wildfly3) ISPN000475: Error processing response for request 1285 from wildfly2: java.lang.ClassNotFoundException: javax.resource.ResourceException from [Module "org.infinispan" version 11.0.8.Final-redhat-00001 from local module loader @5f031ebd (finder: local module finder @4ee37ca3 (roots: /tmp/tests-clustering/jboss-eap-3/modules,/tmp/tests-clustering/jboss-eap-3/modules/system/layers/base))]
       at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:255)
       at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:410)
       at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:398)
       at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:116)
       at java.base/java.lang.Class.forName0(Native Method)
       at java.base/java.lang.Class.forName(Class.java:315)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readGenericThrowable(ThrowableExternalizer.java:278)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:259)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:42)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readWithExternalizer(GlobalMarshaller.java:728)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNonNullableObject(GlobalMarshaller.java:709)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNullableObject(GlobalMarshaller.java:358)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.BytesObjectInput.readObject(BytesObjectInput.java:32)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readGenericThrowable(ThrowableExternalizer.java:276)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:259)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:42)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readWithExternalizer(GlobalMarshaller.java:728)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNonNullableObject(GlobalMarshaller.java:709)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNullableObject(GlobalMarshaller.java:358)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.BytesObjectInput.readObject(BytesObjectInput.java:32)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readGenericThrowable(ThrowableExternalizer.java:276)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:259)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:42)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readWithExternalizer(GlobalMarshaller.java:728)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNonNullableObject(GlobalMarshaller.java:709)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNullableObject(GlobalMarshaller.java:358)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.BytesObjectInput.readObject(BytesObjectInput.java:32)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:233)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.exts.ThrowableExternalizer.readObject(ThrowableExternalizer.java:42)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readWithExternalizer(GlobalMarshaller.java:728)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNonNullableObject(GlobalMarshaller.java:709)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNullableObject(GlobalMarshaller.java:358)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.BytesObjectInput.readObject(BytesObjectInput.java:32)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.remoting.responses.ExceptionResponse$Externalizer.readObject(ExceptionResponse.java:49)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.remoting.responses.ExceptionResponse$Externalizer.readObject(ExceptionResponse.java:41)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readWithExternalizer(GlobalMarshaller.java:728)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNonNullableObject(GlobalMarshaller.java:709)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.readNullableObject(GlobalMarshaller.java:358)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.objectFromObjectInput(GlobalMarshaller.java:192)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.marshall.core.GlobalMarshaller.objectFromByteBuffer(GlobalMarshaller.java:221)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.remoting.transport.jgroups.JGroupsTransport.processResponse(JGroupsTransport.java:1394)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.remoting.transport.jgroups.JGroupsTransport.processMessage(JGroupsTransport.java:1305)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.remoting.transport.jgroups.JGroupsTransport.access$300(JGroupsTransport.java:131)
       at org.infinispan@11.0.8.Final-redhat-00001//org.infinispan.remoting.transport.jgroups.JGroupsTransport$ChannelCallbacks.up(JGroupsTransport.java:1445)

      After discussing the issue with remerson@redhat.com, we can up with a couple of options for how to handle this:

      For the short-term (i.e. 11.0.x), we can handle any ClassNotFoundException thrown during unmarshalling of a generic throwable (i.e. org.infinispan.marshall.exts.ThrowableExternalizer.readGenericThrowable(....)) by returning a RuntimeException whose message contains the message of the original exception, as well as the FQCN of the exception class that could not be resolved.

      In the medium term, I suggested allowing the ThrowableExternalizer to be constructed with a means for resolving classes, e.g. AdvancedExternalizer<ClassLoader>, whose default implementation writes nothing, and when reading returns the ClassLoader associated with the GlobalConfiguration.  The marshalling of a generic throwable will then look something like:

      private final AdvancedExternalizer<ClassLoader> classLoaderExternalizer;
       
      private void writeGenericThrowable(ObjectOutput out, Throwable t) throws IOException {
          Class<?> exceptionClass = t.getClass();
          out.writeUTF(exceptionClass.getName());
          this.classLoaderExternalizer.writeObject(out, exceptionClass.getClassLoader());
          writeMessageAndCause(out, t);
      }
       
      private Throwable readGenericThrowable(ObjectInput in) throws IOException, ClassNotFoundException {
          String impl = in.readUTF();
          ClassLoader loader = this.classLoaderExternalizer.read(in);
          String msg = MarshallUtil.unmarshallString(in);
          Throwable t = (Throwable) in.readObject();
          try {
              Class<?> clazz = loader.loadClass(impl);
              if (t == null && msg == null) {
                  return (Throwable) clazz.getConstructor().newInstance(new Object[]{});
              } else if (t == null) {
                  return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
              } else if (msg == null) {
                  return (Throwable) clazz.getConstructor(Throwable.class).newInstance(t);
              }
              return (Throwable) clazz.getConstructor(String.class, Throwable.class).newInstance(msg, t);
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException e) {
              throw new MarshallingException(e);
          }
      }
      

       

      In the long-term, this stuff will be replaced by ProtoStream, though that mechanism should still provide a means for overriding how a given exception class is resolved.

              Unassigned Unassigned
              pferraro@redhat.com Paul Ferraro
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Created:
                Updated: