diff --git a/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java b/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java index 082f058e66..0d07283298 100644 --- a/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java +++ b/parquet-common/src/main/java/org/apache/parquet/util/DynMethods.java @@ -243,7 +243,7 @@ public Builder impl(String className, String methodName, Class... argClasses) try { Class targetClass = Class.forName(className, true, loader); impl(targetClass, methodName, argClasses); - } catch (ClassNotFoundException e) { + } catch (ClassNotFoundException | NoClassDefFoundError e) { // class not found on supplied classloader. LOG.debug("failed to load class {}", className, e); } @@ -352,7 +352,7 @@ public Builder hiddenImpl(String className, String methodName, Class... argCl try { Class targetClass = Class.forName(className, true, loader); hiddenImpl(targetClass, methodName, argClasses); - } catch (ClassNotFoundException e) { + } catch (ClassNotFoundException | NoClassDefFoundError e) { // class not found on supplied classloader. LOG.debug("failed to load class {}", className, e); } diff --git a/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java b/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java index 26124b9c81..9327660f88 100644 --- a/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java +++ b/parquet-common/src/test/java/org/apache/parquet/util/TestDynConstructors.java @@ -19,6 +19,9 @@ package org.apache.parquet.util; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import java.util.concurrent.Callable; import org.apache.parquet.TestUtils; import org.apache.parquet.util.Concatenator.SomeCheckedException; @@ -166,4 +169,63 @@ public void testInvoke() throws Exception { Assert.assertNotNull("Should allow invokeChecked(null, ...)", ctor.invokeChecked(null)); Assert.assertNotNull("Should allow invoke(null, ...)", ctor.invoke(null)); } + + @Test + public void implWithNoClassDefFoundError() throws Exception { + ClassLoader errorLoader = new ClassLoader(Thread.currentThread().getContextClassLoader()) { + @Override + public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if ("org.apache.parquet.MissingDependencyClass".equals(name)) { + throw new NoClassDefFoundError("some/TransitiveDependency"); + } + + return super.loadClass(name, resolve); + } + }; + + assertThatThrownBy(() -> new DynConstructors.Builder(MyInterface.class) + .loader(errorLoader) + .impl("org.apache.parquet.MissingDependencyClass") + .buildChecked()) + .isInstanceOf(NoSuchMethodException.class) + .hasMessageStartingWith("Cannot find constructor for interface") + .hasMessageContaining("Missing org.apache.parquet.MissingDependencyClass"); + + assertThat(new DynConstructors.Builder(MyInterface.class) + .loader(errorLoader) + .impl("org.apache.parquet.MissingDependencyClass") + .impl(MyClass.class) + .buildChecked() + .newInstance()) + .isInstanceOf(MyClass.class); + + assertThat(new DynConstructors.Builder(MyInterface.class) + .loader(errorLoader) + .hiddenImpl("org.apache.parquet.MissingDependencyClass") + .impl(MyClass.class) + .buildChecked() + .newInstance()) + .isInstanceOf(MyClass.class); + } + + @Test + public void implWithExceptionInInitializerError() { + ClassLoader errorLoader = new ClassLoader(Thread.currentThread().getContextClassLoader()) { + @Override + public Class loadClass(String name, boolean resolve) { + throw new ExceptionInInitializerError("static initializer failed"); + } + }; + + assertThatThrownBy(() -> new DynConstructors.Builder(MyInterface.class) + .loader(errorLoader) + .impl("org.apache.parquet.FailingInitClass") + .buildChecked()) + .isInstanceOf(ExceptionInInitializerError.class) + .hasMessage("static initializer failed"); + } + + public interface MyInterface {} + + public static class MyClass implements MyInterface {} } diff --git a/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java b/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java index 94e9ed93a9..59c6d37167 100644 --- a/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java +++ b/parquet-common/src/test/java/org/apache/parquet/util/TestDynMethods.java @@ -19,6 +19,9 @@ package org.apache.parquet.util; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import java.util.concurrent.Callable; import org.apache.parquet.TestUtils; import org.apache.parquet.util.Concatenator.SomeCheckedException; @@ -306,4 +309,69 @@ public void testNoop() throws Exception { Assert.assertNull("NOOP can be bound to null", noop.bind(null).invoke("a")); Assert.assertNull("NOOP can be static", noop.asStatic().invoke("a")); } + + static class Available { + public static String register() { + return "available"; + } + + @SuppressWarnings("unused") + private static String hiddenRegister() { + return "hidden-available"; + } + } + + @Test + public void implWithNoClassDefFoundError() throws NoSuchMethodException { + ClassLoader errorLoader = new ClassLoader(Thread.currentThread().getContextClassLoader()) { + @Override + public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if ("org.apache.parquet.MissingDependencyClass".equals(name)) { + throw new NoClassDefFoundError("some/TransitiveDependency"); + } + + return super.loadClass(name, resolve); + } + }; + + assertThatThrownBy(() -> new DynMethods.Builder("register") + .loader(errorLoader) + .impl("org.apache.parquet.MissingDependencyClass") + .buildStaticChecked()) + .isInstanceOf(NoSuchMethodException.class) + .hasMessage("Cannot find method: register"); + + assertThat(new DynMethods.Builder("register") + .loader(errorLoader) + .impl("org.apache.parquet.MissingDependencyClass") + .impl(Available.class) + .buildStaticChecked() + .invoke()) + .isEqualTo("available"); + + assertThat(new DynMethods.Builder("register") + .loader(errorLoader) + .hiddenImpl("org.apache.parquet.MissingDependencyClass") + .hiddenImpl(Available.class, "hiddenRegister") + .buildStaticChecked() + .invoke()) + .isEqualTo("hidden-available"); + } + + @Test + public void implWithExceptionInInitializerError() { + ClassLoader errorLoader = new ClassLoader(Thread.currentThread().getContextClassLoader()) { + @Override + public Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + throw new ExceptionInInitializerError("static initializer failed"); + } + }; + + assertThatThrownBy(() -> new DynMethods.Builder("register") + .loader(errorLoader) + .impl("org.apache.parquet.FailingInitClass") + .buildStaticChecked()) + .isInstanceOf(ExceptionInInitializerError.class) + .hasMessage("static initializer failed"); + } }