This is an automated email from the ASF dual-hosted git repository. lidavidm pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/arrow.git
The following commit(s) were added to refs/heads/main by this push: new 3217fc2625 GH-44564: [Java][FlightSQL] Fix native libraries relocation (#44565) 3217fc2625 is described below commit 3217fc2625f80042661172fd589a70c270ba8f8b Author: Laurent Goujon <lauren...@users.noreply.github.com> AuthorDate: Tue Oct 29 21:15:50 2024 -0700 GH-44564: [Java][FlightSQL] Fix native libraries relocation (#44565) ### Rationale for this change Prefix used by native libraries shipped in the JDBC driver do not match the prefix used by Netty `NativeLibraryLoader` class, preventing them to be detected and loaded. ### What changes are included in this PR? Change the prefix of the libraries and add a integration test to verify the libraries are loaded Also exclude several group of data which are not properly relocated and may cause conflict with existing classpath. ### Are these changes tested? Yes, new test added for checking the native library loader ### Are there any user-facing changes? No * GitHub Issue: #44564 Authored-by: Laurent Goujon <laur...@apache.org> Signed-off-by: David Li <li.david...@gmail.com> --- java/flight/flight-sql-jdbc-driver/pom.xml | 13 +-- .../arrow/driver/jdbc/ITDriverJarValidation.java | 96 ++++++++++++++++------ 2 files changed, 79 insertions(+), 30 deletions(-) diff --git a/java/flight/flight-sql-jdbc-driver/pom.xml b/java/flight/flight-sql-jdbc-driver/pom.xml index ba6fe277d1..ae8c543fbf 100644 --- a/java/flight/flight-sql-jdbc-driver/pom.xml +++ b/java/flight/flight-sql-jdbc-driver/pom.xml @@ -127,14 +127,14 @@ under the License. <pattern>mozilla.</pattern> <shadedPattern>org.apache.arrow.driver.jdbc.shaded.mozilla.</shadedPattern> </relocation> - <!-- Entries to relocate netty native libraries --> + <!-- Entries to relocate netty native libraries. Prefix has to match relocation prefix (dots replaced with underscore) --> <relocation> <pattern>META-INF.native.libnetty_</pattern> - <shadedPattern>META-INF.native.liboaadj_netty_</shadedPattern> + <shadedPattern>META-INF.native.liborg_apache_arrow_driver_jdbc_shaded_netty_</shadedPattern> </relocation> <relocation> <pattern>META-INF.native.netty_</pattern> - <shadedPattern>META-INF.native.oaadj_netty_</shadedPattern> + <shadedPattern>META-INF.native.org_apache_arrow_driver_jdbc_shaded_netty_</shadedPattern> </relocation> </relocations> <transformers> @@ -159,8 +159,11 @@ under the License. <exclude>**/*.SF</exclude> <exclude>**/*.RSA</exclude> <exclude>**/*.DSA</exclude> - <exclude>META-INF/native/libio_grpc_netty*</exclude> - <exclude>META-INF/native/io_grpc_netty_shaded*</exclude> + <!-- Requires some resource transformer --> + <exclude>META-INF/native-image/</exclude> + <exclude>META-INF/proguard/</exclude> + <!-- Requires MSHADE-406 --> + <exclude>META-INF/versions/</exclude> <exclude>**/*.proto</exclude> <exclude>**/module-info.class</exclude> </excludes> diff --git a/java/flight/flight-sql-jdbc-driver/src/test/java/org/apache/arrow/driver/jdbc/ITDriverJarValidation.java b/java/flight/flight-sql-jdbc-driver/src/test/java/org/apache/arrow/driver/jdbc/ITDriverJarValidation.java index b45845485a..a0e108d6a0 100644 --- a/java/flight/flight-sql-jdbc-driver/src/test/java/org/apache/arrow/driver/jdbc/ITDriverJarValidation.java +++ b/java/flight/flight-sql-jdbc-driver/src/test/java/org/apache/arrow/driver/jdbc/ITDriverJarValidation.java @@ -18,20 +18,26 @@ package org.apache.arrow.driver.jdbc; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; import com.google.common.collect.ImmutableSet; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.JarURLConnection; +import java.net.URISyntaxException; import java.net.URL; -import java.util.Enumeration; +import java.net.URLClassLoader; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.stream.Stream; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.api.function.Executable; /** * Check the content of the JDBC driver jar @@ -48,52 +54,92 @@ public class ITDriverJarValidation { /** List of allowed prefixes a jar entry may match. */ public static final Set<String> ALLOWED_PREFIXES = - ImmutableSet.of("org/apache/arrow/driver/jdbc/", "META-INF/"); + ImmutableSet.of( + "org/apache/arrow/driver/jdbc/", // Driver code + "META-INF/maven/", // Maven metadata (useful for security scanner + "META-INF/services/", // ServiceLoader implementations + "META-INF/license/", + "META-INF/licenses/", + // Prefixes for native libraries + "META-INF/native/liborg_apache_arrow_driver_jdbc_shaded_", + "META-INF/native/org_apache_arrow_driver_jdbc_shaded_"); /** List of allowed files a jar entry may match. */ public static final Set<String> ALLOWED_FILES = - ImmutableSet.of("arrow-git.properties", "properties/flight.properties"); + ImmutableSet.of( + "arrow-git.properties", + "properties/flight.properties", + "META-INF/io.netty.versions.properties", + "META-INF/MANIFEST.MF", + "META-INF/DEPENDENCIES", + "META-INF/FastDoubleParser-LICENSE", + "META-INF/FastDoubleParser-NOTICE", + "META-INF/LICENSE", + "META-INF/LICENSE.txt", + "META-INF/NOTICE", + "META-INF/NOTICE.txt", + "META-INF/thirdparty-LICENSE", + "META-INF/bigint-LICENSE"); // This method is designed to work with Maven failsafe plugin and expects the // JDBC driver jar to be present in the test classpath (instead of the individual classes) - private static JarFile getJdbcJarFile() throws IOException { + private static File getJdbcJarFile() throws IOException { // Check if an override has been set if (JDBC_DRIVER_PATH_OVERRIDE != null) { - return new JarFile(new File(JDBC_DRIVER_PATH_OVERRIDE)); + return new File(JDBC_DRIVER_PATH_OVERRIDE); } - // Check classpath to find the driver jar + // Check classpath to find the driver jar (without loading the class) URL driverClassURL = ITDriverJarValidation.class .getClassLoader() .getResource("org/apache/arrow/driver/jdbc/ArrowFlightJdbcDriver.class"); - assertNotNull(driverClassURL, "Driver jar was not detected in the classpath"); + assertNotNull(driverClassURL, "Driver class was not detected in the classpath"); assertEquals( - "jar", driverClassURL.getProtocol(), "Driver jar was not detected in the classpath"); + "jar", driverClassURL.getProtocol(), "Driver class was not found inside a jar file"); + // Return the enclosing jar file JarURLConnection connection = (JarURLConnection) driverClassURL.openConnection(); - return connection.getJarFile(); + try { + return new File(connection.getJarFileURL().toURI()); + } catch (URISyntaxException e) { + throw new IOException(e); + } } + /** Validate the content of the jar to enforce all 3rd party dependencies have been shaded. */ @Test @Timeout(value = 2, unit = TimeUnit.MINUTES) public void validateShadedJar() throws IOException { - // Validate the content of the jar to enforce all 3rd party dependencies have - // been shaded - try (JarFile jar = getJdbcJarFile()) { - for (Enumeration<JarEntry> entries = jar.entries(); entries.hasMoreElements(); ) { - final JarEntry entry = entries.nextElement(); - if (entry.isDirectory()) { - // Directories are ignored - continue; - } - - try { - checkEntryAllowed(entry.getName()); - } catch (AssertionError e) { - fail(e.getMessage()); - } + + try (JarFile jar = new JarFile(getJdbcJarFile())) { + Stream<Executable> executables = + jar.stream() + .filter(Predicate.not(JarEntry::isDirectory)) + .map( + entry -> { + return () -> checkEntryAllowed(entry.getName()); + }); + + Assertions.assertAll(executables); + } + } + + /** Check that relocated netty code can also load matching native library. */ + @Test + @Timeout(value = 2, unit = TimeUnit.MINUTES) + public void checkNettyOpenSslNativeLoader() throws Throwable { + try (URLClassLoader driverClassLoader = + new URLClassLoader(new URL[] {getJdbcJarFile().toURI().toURL()}, null)) { + Class<?> openSslClass = + driverClassLoader.loadClass( + "org.apache.arrow.driver.jdbc.shaded.io.netty.handler.ssl.OpenSsl"); + Method method = openSslClass.getDeclaredMethod("ensureAvailability"); + try { + method.invoke(null); + } catch (InvocationTargetException e) { + throw e.getCause(); } } }