This is an automated email from the ASF dual-hosted git repository.
cgivre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git
The following commit(s) were added to refs/heads/master by this push:
new d2d6f10793 DRILL-8553: Add Support for Java 25 LTS (#3066)
d2d6f10793 is described below
commit d2d6f10793e146b5cd36c4520f5e11cd8c5d11cd
Author: Charles S. Givre <[email protected]>
AuthorDate: Wed Aug 19 11:38:29 2026 -0400
DRILL-8553: Add Support for Java 25 LTS (#3066)
* Add CI/CD for Java 25
* Update pom.xml
* Bump Hadoop to 3.4.3
* Minor Fixes
* Fix one more error
* Various fixes
* Fix Phoenix Tests
---
.github/workflows/ci.yml | 2 +-
contrib/storage-phoenix/pom.xml | 3 +
.../org/apache/drill/exec/compile/AsmUtil.java | 2 +-
.../drill/exec/compile/DrillCheckClassAdapter.java | 82 ++++++++++++++++++++--
.../rpc/security/kerberos/KerberosFactory.java | 5 +-
.../org/apache/drill/exec/rpc/user/UserClient.java | 20 +++++-
.../exec/compile/bytecode/ReplaceMethodInvoke.java | 2 +-
.../rpc/user/security/TestUserBitKerberos.java | 7 +-
.../security/TestUserBitKerberosEncryption.java | 3 +-
.../rest/spnego/TestDrillSpnegoAuthenticator.java | 3 +-
.../rest/spnego/TestSpnegoAuthentication.java | 3 +-
pom.xml | 21 ++----
12 files changed, 118 insertions(+), 35 deletions(-)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5280e4c735..834b7933cd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,7 @@ jobs:
strategy:
matrix:
# Java versions to run unit tests (Jetty 12 requires Java 17+)
- java: [ '17', '21' ]
+ java: [ '17', '21', '25' ]
profile: ['default-hadoop']
fail-fast: false
steps:
diff --git a/contrib/storage-phoenix/pom.xml b/contrib/storage-phoenix/pom.xml
index 3ac604f627..f12766ee75 100644
--- a/contrib/storage-phoenix/pom.xml
+++ b/contrib/storage-phoenix/pom.xml
@@ -335,6 +335,9 @@
-Djava.net.preferIPv4Stack=true
-Dsun.security.krb5.debug=true
-Dsun.security.krb5.allowUdp=false
+ <!-- HBase's shaded Netty disables sun.misc.Unsafe by default on
Java 24+, but HBase's own
+ ByteBuff/UnsafeAccess still calls into it, so the mini
cluster NPEs on every RPC. -->
+ -Dorg.apache.hbase.thirdparty.io.netty.noUnsafe=false
</argLine>
</configuration>
</plugin>
diff --git
a/exec/java-exec/src/main/java/org/apache/drill/exec/compile/AsmUtil.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/compile/AsmUtil.java
index fd071d6230..eff29e088e 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/compile/AsmUtil.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/compile/AsmUtil.java
@@ -53,7 +53,7 @@ public class AsmUtil {
classNode.accept(verifyWriter);
final ClassReader ver = new ClassReader(verifyWriter.toByteArray());
try {
- DrillCheckClassAdapter.verify(ver, false, new PrintWriter(sw));
+ DrillCheckClassAdapter.verify(ver, new PrintWriter(sw));
} catch(final Exception e) {
logger.info("Caught exception verifying class:");
logClass(logger, logTag, classNode);
diff --git
a/exec/java-exec/src/main/java/org/apache/drill/exec/compile/DrillCheckClassAdapter.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/compile/DrillCheckClassAdapter.java
index 4076c2379c..bf632d7004 100644
---
a/exec/java-exec/src/main/java/org/apache/drill/exec/compile/DrillCheckClassAdapter.java
+++
b/exec/java-exec/src/main/java/org/apache/drill/exec/compile/DrillCheckClassAdapter.java
@@ -18,10 +18,20 @@
package org.apache.drill.exec.compile;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.analysis.Analyzer;
+import org.objectweb.asm.tree.analysis.AnalyzerException;
+import org.objectweb.asm.tree.analysis.BasicValue;
+import org.objectweb.asm.tree.analysis.SimpleVerifier;
import org.objectweb.asm.util.CheckClassAdapter;
/**
@@ -100,19 +110,79 @@ public class DrillCheckClassAdapter extends
RetargetableClassVisitor {
}
/**
- * See {@link org.objectweb.asm.util.CheckClassAdapter#verify(ClassReader,
boolean, PrintWriter)}.
+ * Data flow verification, equivalent to
+ * {@link org.objectweb.asm.util.CheckClassAdapter#verify(ClassReader,
boolean, PrintWriter)}
+ * but tolerant of types that cannot be loaded (see {@link LenientVerifier}).
+ * Any problem found is written to <code>pw</code>; nothing is written if the
+ * class is well formed.
*/
- public static void verify(final ClassReader cr, final boolean dump,
- final PrintWriter pw) {
+ public static void verify(final ClassReader cr, final PrintWriter pw) {
/*
* For plain verification, we don't need to restore the original access
* bytes the way we do when the check adapter is used as part of a chain,
so
- * we can just strip it and use the ASM version directly.
+ * we can just strip it and verify directly.
*/
final ClassWriter classWriter = new ClassWriter(0);
cr.accept(new InnerClassAccessStripper(CompilationConfig.ASM_API_VERSION,
classWriter), ClassReader.SKIP_DEBUG);
- final ClassReader strippedCr = new ClassReader(classWriter.toByteArray());
- CheckClassAdapter.verify(strippedCr, dump, pw);
+
+ final ClassNode classNode = new ClassNode();
+ new ClassReader(classWriter.toByteArray()).accept(classNode,
ClassReader.SKIP_DEBUG);
+
+ final Type currentClass = Type.getObjectType(classNode.name);
+ final Type currentSuperClass =
+ classNode.superName == null ? null :
Type.getObjectType(classNode.superName);
+ final List<Type> currentClassInterfaces = new ArrayList<>();
+ for (String interfaceName : classNode.interfaces) {
+ currentClassInterfaces.add(Type.getObjectType(interfaceName));
+ }
+ final boolean isInterface = (classNode.access & Opcodes.ACC_INTERFACE) !=
0;
+
+ for (MethodNode method : classNode.methods) {
+ final SimpleVerifier verifier = new LenientVerifier(
+ currentClass, currentSuperClass, currentClassInterfaces,
isInterface);
+ try {
+ new Analyzer<>(verifier).analyze(classNode.name, method);
+ } catch (AnalyzerException e) {
+ e.printStackTrace(pw);
+ }
+ }
+ }
+
+ /**
+ * ASM's {@link SimpleVerifier} resolves types with {@link Class#forName},
which
+ * cannot work for the classes Drill is in the middle of generating: a
generated
+ * nested class refers to its enclosing generated class, and neither has been
+ * defined in any class loader yet. Since JDK 22 javac emits an
+ * <code>Objects.requireNonNull(outer)</code> prologue in nested class
+ * constructors, which makes the verifier resolve the enclosing class and
fail.
+ *
+ * <p>Types that cannot be loaded are treated as assignable, so verification
+ * still covers everything that is resolvable.
+ */
+ private static class LenientVerifier extends SimpleVerifier {
+ LenientVerifier(final Type currentClass, final Type currentSuperClass,
+ final List<Type> currentClassInterfaces, final boolean isInterface) {
+ super(CompilationConfig.ASM_API_VERSION, currentClass, currentSuperClass,
+ currentClassInterfaces, isInterface);
+ }
+
+ @Override
+ protected boolean isAssignableFrom(final Type type1, final Type type2) {
+ try {
+ return super.isAssignableFrom(type1, type2);
+ } catch (TypeNotPresentException e) {
+ return true;
+ }
+ }
+
+ @Override
+ public BasicValue merge(final BasicValue value1, final BasicValue value2) {
+ try {
+ return super.merge(value1, value2);
+ } catch (TypeNotPresentException e) {
+ return BasicValue.REFERENCE_VALUE;
+ }
+ }
}
}
diff --git
a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/security/kerberos/KerberosFactory.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/security/kerberos/KerberosFactory.java
index 98b4793d75..777a637977 100644
---
a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/security/kerberos/KerberosFactory.java
+++
b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/security/kerberos/KerberosFactory.java
@@ -26,9 +26,9 @@ import
org.apache.drill.exec.rpc.security.SecurityConfiguration;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.security.HadoopKerberosName;
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
import org.apache.hadoop.security.UserGroupInformation;
-import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
@@ -41,7 +41,6 @@ import javax.security.sasl.SaslServer;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
-import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.Map;
@@ -68,7 +67,7 @@ public class KerberosFactory implements AuthenticatorFactory {
try {
final UserGroupInformation ugi;
if (assumeSubject) {
- ugi =
UserGroupInformation.getUGIFromSubject(Subject.getSubject(AccessController.getContext()));
+ ugi = UserGroupInformation.getUGIFromSubject(SubjectUtil.current());
logger.debug("Assuming subject for {}.", ugi.getShortUserName());
} else {
if (keytab != null) {
diff --git
a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java
b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java
index 3f12a1e1d6..c2be2e1525 100644
---
a/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java
+++
b/exec/java-exec/src/main/java/org/apache/drill/exec/rpc/user/UserClient.java
@@ -79,10 +79,14 @@ import
org.apache.drill.exec.rpc.security.plain.PlainFactory;
import org.apache.drill.exec.ssl.SSLConfig;
import org.apache.drill.exec.ssl.SSLConfigBuilder;
import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
import org.slf4j.Logger;
import javax.net.ssl.SSLEngine;
+import javax.security.auth.Subject;
import javax.security.sasl.SaslException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -110,6 +114,11 @@ public class UserClient
private DrillProperties properties;
+ // ponytail: the SASL handshake completes on a Netty thread, which no longer
inherits the caller's
+ // Subject (JEP 486 replaced the inheritable AccessControlContext with a
scoped value). Capture the
+ // Subject on the connecting thread and rebind it around the login below.
+ private Subject subject;
+
public UserClient(String clientName, DrillConfig config, Properties
properties, boolean supportComplexTypes,
BufferAllocator allocator, EventLoopGroup eventLoopGroup, Executor
eventExecutor,
DrillbitEndpoint endpoint) throws NonTransientRpcException {
@@ -174,6 +183,7 @@ public class UserClient
*/
public void connect(final DrillbitEndpoint endpoint, final DrillProperties
properties,
final UserCredentials credentials) throws RpcException {
+ subject = SubjectUtil.current();
final UserToBitHandshake.Builder hsBuilder =
UserToBitHandshake.newBuilder()
.setRpcVersion(UserRpcConfig.RPC_VERSION)
@@ -449,7 +459,15 @@ public class UserClient
final ClassLoader oldThreadCtxtCL =
Thread.currentThread().getContextClassLoader();
final ClassLoader newThreadCtxtCL = this.getClass().getClassLoader();
Thread.currentThread().setContextClassLoader(newThreadCtxtCL);
- final UserGroupInformation ugi =
factory.createAndLoginUser(saslProperties);
+ final UserGroupInformation ugi;
+ try {
+ ugi = SubjectUtil.doAs(subject,
+ (PrivilegedExceptionAction<UserGroupInformation>) () ->
factory.createAndLoginUser(saslProperties));
+ } catch (PrivilegedActionException e) {
+ Thread.currentThread().setContextClassLoader(oldThreadCtxtCL);
+ throw e.getCause() instanceof IOException
+ ? (IOException) e.getCause() : new IOException(e.getCause());
+ }
// Reset the thread context class loader to original one
Thread.currentThread().setContextClassLoader(oldThreadCtxtCL);
diff --git
a/exec/java-exec/src/test/java/org/apache/drill/exec/compile/bytecode/ReplaceMethodInvoke.java
b/exec/java-exec/src/test/java/org/apache/drill/exec/compile/bytecode/ReplaceMethodInvoke.java
index 879dc4d156..500a4b1d02 100644
---
a/exec/java-exec/src/test/java/org/apache/drill/exec/compile/bytecode/ReplaceMethodInvoke.java
+++
b/exec/java-exec/src/test/java/org/apache/drill/exec/compile/bytecode/ReplaceMethodInvoke.java
@@ -73,7 +73,7 @@ public class ReplaceMethodInvoke {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter(sw);
- DrillCheckClassAdapter.verify(new ClassReader(cw.toByteArray()), false,
pw);
+ DrillCheckClassAdapter.verify(new ClassReader(cw.toByteArray()), pw);
final String checkString = sw.toString();
if (!checkString.isEmpty()) {
diff --git
a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java
b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java
index 0783b44982..39183d87e0 100644
---
a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java
+++
b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberos.java
@@ -40,6 +40,7 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import javax.security.auth.Subject;
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
import java.security.PrivilegedExceptionAction;
import static junit.framework.TestCase.assertEquals;
@@ -100,7 +101,7 @@ public class TestUserBitKerberos extends ClusterTest {
);
try (
- ClientFixture client = Subject.doAs(
+ ClientFixture client = SubjectUtil.doAs(
clientSubject,
(PrivilegedExceptionAction<ClientFixture>) () ->
cluster.clientBuilder()
.property(DrillProperties.SERVICE_PRINCIPAL,
krbHelper.SERVER_PRINCIPAL)
@@ -136,7 +137,7 @@ public class TestUserBitKerberos extends ClusterTest {
try (
// Use a dedicated cluster fixture so that the tested RPC counters have
a clean start.
ClusterFixture cluster = defaultClusterConfig().build();
- ClientFixture client = Subject.doAs(
+ ClientFixture client = SubjectUtil.doAs(
clientSubject,
(PrivilegedExceptionAction<ClientFixture>) () ->
cluster.clientBuilder()
.property(DrillProperties.SERVICE_PRINCIPAL,
krbHelper.SERVER_PRINCIPAL)
@@ -178,7 +179,7 @@ public class TestUserBitKerberos extends ClusterTest {
try (
// Use a dedicated cluster fixture so that the tested RPC counters have
a clean start.
ClusterFixture cluster = defaultClusterConfig().build();
- ClientFixture client = Subject.doAs(
+ ClientFixture client = SubjectUtil.doAs(
clientSubject,
(PrivilegedExceptionAction<ClientFixture>) () ->
cluster.clientBuilder()
.property(DrillProperties.SERVICE_PRINCIPAL,
krbHelper.SERVER_PRINCIPAL)
diff --git
a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java
b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java
index 5f7b0f39ca..560dd46e44 100644
---
a/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java
+++
b/exec/java-exec/src/test/java/org/apache/drill/exec/rpc/user/security/TestUserBitKerberosEncryption.java
@@ -42,6 +42,7 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import javax.security.auth.Subject;
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
import java.security.PrivilegedExceptionAction;
import static junit.framework.TestCase.assertEquals;
@@ -157,7 +158,7 @@ public class TestUserBitKerberosEncryption extends
ClusterTest {
);
try (
- ClientFixture client = Subject.doAs(
+ ClientFixture client = SubjectUtil.doAs(
clientSubject,
(PrivilegedExceptionAction<ClientFixture>) () ->
cluster.clientBuilder()
.property(DrillProperties.SERVICE_PRINCIPAL,
krbHelper.SERVER_PRINCIPAL)
diff --git
a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
index c0b8b617c0..5d86c0f08f 100644
---
a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
+++
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestDrillSpnegoAuthenticator.java
@@ -45,6 +45,7 @@ import org.junit.Test;
import org.junit.experimental.categories.Category;
import javax.security.auth.Subject;
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
import java.lang.reflect.Field;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.TimeUnit;
@@ -118,7 +119,7 @@ public class TestDrillSpnegoAuthenticator extends
ClusterTest {
final Subject clientSubject =
JaasKrbUtil.loginUsingKeytab(spnegoHelper.CLIENT_PRINCIPAL,
spnegoHelper.clientKeytab.getAbsoluteFile());
- return Subject.doAs(clientSubject, (PrivilegedExceptionAction<String>) ()
-> {
+ return SubjectUtil.doAs(clientSubject, (PrivilegedExceptionAction<String>)
() -> {
final GSSManager gssManager = GSSManager.getInstance();
GSSContext gssContext = null;
try {
diff --git
a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
index cf8f38b84a..678f35574c 100644
---
a/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
+++
b/exec/java-exec/src/test/java/org/apache/drill/exec/server/rest/spnego/TestSpnegoAuthentication.java
@@ -53,6 +53,7 @@ import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
import javax.security.auth.Subject;
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
import java.lang.reflect.Field;
import java.security.PrivilegedExceptionAction;
@@ -255,7 +256,7 @@ public class TestSpnegoAuthentication extends BaseTest {
spnegoHelper.clientKeytab.getAbsoluteFile());
// Generate a SPNEGO token for the peer SERVER_PRINCIPAL from this
CLIENT_PRINCIPAL
- final String token = Subject.doAs(clientSubject, new
PrivilegedExceptionAction<String>() {
+ final String token = SubjectUtil.doAs(clientSubject, new
PrivilegedExceptionAction<String>() {
@Override
public String run() throws Exception {
diff --git a/pom.xml b/pom.xml
index ed185fe1e1..08688e4de3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,7 +82,9 @@
<forkCount>1</forkCount>
<freemarker.version>2.3.30</freemarker.version>
<guava.version>32.1.2-jre</guava.version>
- <hadoop.version>3.4.1</hadoop.version>
+ <!-- 3.4.3+ required on JDK 24+: earlier versions call
Subject.getSubject(AccessControlContext),
+ which JEP 486 made throw UnsupportedOperationException
unconditionally. -->
+ <hadoop.version>3.4.3</hadoop.version>
<hamcrest.version>2.2</hamcrest.version>
<hbase.version>2.6.1-hadoop3</hbase.version>
<hikari.version>4.0.3</hikari.version>
@@ -123,8 +125,7 @@
<maven.version>3.8.4</maven.version>
<memoryMb>4096</memoryMb>
<metrics.version>4.2.19</metrics.version>
- <mockito.version>5.17.0</mockito.version>
- <mockito_inline.version>5.2.0</mockito_inline.version>
+ <mockito.version>5.23.0</mockito.version>
<mongo.version>5.5.1</mongo.version>
<msgpack.version>0.6.6</msgpack.version>
<nashorn.version>15.4</nashorn.version>
@@ -510,7 +511,7 @@
<version>[${maven.version.min},4)</version>
</requireMavenVersion>
<requireJavaVersion>
- <version>[17,24)</version>
+ <version>[17,26)</version>
</requireJavaVersion>
</rules>
</configuration>
@@ -932,18 +933,6 @@
</exclusions>
<scope>test</scope>
</dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-inline</artifactId>
- <version>${mockito_inline.version}</version>
- <scope>test</scope>
- <exclusions>
- <exclusion>
- <artifactId>mockito-core</artifactId>
- <groupId>org.mockito</groupId>
- </exclusion>
- </exclusions>
- </dependency>
<dependency>
<groupId>de.huxhorn.lilith</groupId>
<artifactId>de.huxhorn.lilith.logback.appender.multiplex-classic</artifactId>