This is an automated email from the ASF dual-hosted git repository. slfan1989 pushed a commit to branch branch-3.4 in repository https://gitbox.apache.org/repos/asf/hadoop.git
The following commit(s) were added to refs/heads/branch-3.4 by this push: new e4532f224d4 HADOOP-19212. Hadoop UGI compatible with Java 25 (#7886) (#7897) e4532f224d4 is described below commit e4532f224d496c766de2fa393f567e082d1288da Author: Cheng Pan <cheng...@apache.org> AuthorDate: Wed Aug 27 10:06:50 2025 +0800 HADOOP-19212. Hadoop UGI compatible with Java 25 (#7886) (#7897) * HADOOP-19212. Hadoop UGI compatible with Java 25. Co-authored-by: Istvan Toth <st...@apache.org> Signed-off-by: Shilun Fan <slfan1...@apache.org> --- .../client/KerberosAuthenticator.java | 6 +- .../security/authentication/util/SubjectUtil.java | 308 +++++++++++++++++++ .../authentication/util/TestSubjectUtil.java | 336 +++++++++++++++++++++ .../hadoop/security/UserGroupInformation.java | 12 +- 4 files changed, 651 insertions(+), 11 deletions(-) diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java index 30e65efe10c..d27b93bd50c 100644 --- a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java +++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java @@ -19,6 +19,7 @@ import org.apache.hadoop.security.authentication.server.HttpConstants; import org.apache.hadoop.security.authentication.util.AuthToken; import org.apache.hadoop.security.authentication.util.KerberosUtil; +import org.apache.hadoop.security.authentication.util.SubjectUtil; import org.ietf.jgss.GSSContext; import org.ietf.jgss.GSSManager; import org.ietf.jgss.GSSName; @@ -35,8 +36,6 @@ import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; -import java.security.AccessControlContext; -import java.security.AccessController; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; import java.util.HashMap; @@ -300,8 +299,7 @@ private boolean isNegotiate(HttpURLConnection conn) throws IOException { private void doSpnegoSequence(final AuthenticatedURL.Token token) throws IOException, AuthenticationException { try { - AccessControlContext context = AccessController.getContext(); - Subject subject = Subject.getSubject(context); + Subject subject = SubjectUtil.current(); if (subject == null || (!KerberosUtil.hasKerberosKeyTab(subject) && !KerberosUtil.hasKerberosTicket(subject))) { diff --git a/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SubjectUtil.java b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SubjectUtil.java new file mode 100644 index 00000000000..faf2d6c7d81 --- /dev/null +++ b/hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/util/SubjectUtil.java @@ -0,0 +1,308 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.security.authentication.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +/** + * An utility class that adapts the Security Manager and APIs related to it for + * JDK 8 and above. + * <p> + * In JDK 17, the Security Manager and APIs related to it have been deprecated + * and are subject to removal in a future release. There is no replacement for + * the Security Manager. See <a href="https://openjdk.org/jeps/411">JEP 411</a> + * for discussion and alternatives. + * <p> + * In JDK 24, the Security Manager has been permanently disabled. See + * <a href="https://openjdk.org/jeps/486">JEP 486</a> for more information. + * <p> + * This is derived from Apache Calcite Avatica, which is derived from the Jetty + * implementation. + */ +@Private +public final class SubjectUtil { + private static final MethodHandle CALL_AS = lookupCallAs(); + static final boolean HAS_CALL_AS = CALL_AS != null; + private static final MethodHandle DO_AS = HAS_CALL_AS ? null : lookupDoAs(); + private static final MethodHandle DO_AS_THROW_EXCEPTION = + HAS_CALL_AS ? null : lookupDoAsThrowException(); + private static final MethodHandle CURRENT = lookupCurrent(); + + private static MethodHandle lookupCallAs() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + try { + // Subject.callAs() is available since Java 18. + return lookup.findStatic(Subject.class, "callAs", + MethodType.methodType(Object.class, Subject.class, Callable.class)); + } catch (NoSuchMethodException x) { + return null; + } + } catch (IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static MethodHandle lookupDoAs() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + MethodType signature = MethodType.methodType( + Object.class, Subject.class, PrivilegedAction.class); + return lookup.findStatic(Subject.class, "doAs", signature); + } catch (IllegalAccessException | NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static MethodHandle lookupDoAsThrowException() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + MethodType signature = MethodType.methodType( + Object.class, Subject.class, PrivilegedExceptionAction.class); + return lookup.findStatic(Subject.class, "doAs", signature); + } catch (IllegalAccessException | NoSuchMethodException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static MethodHandle lookupCurrent() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + // Subject.getSubject(AccessControlContext) is deprecated for removal and + // replaced by Subject.current(). + // Lookup first the new API, since for Java versions where both exists, the + // new API delegates to the old API (e.g. Java 18, 19 and 20). + // Otherwise (e.g. Java 17), lookup the old API. + return lookup.findStatic( + Subject.class, "current", MethodType.methodType(Subject.class)); + } catch (NoSuchMethodException e) { + MethodHandle getContext = lookupGetContext(); + MethodHandle getSubject = lookupGetSubject(); + return MethodHandles.filterReturnValue(getContext, getSubject); + } catch (IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static MethodHandle lookupGetSubject() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + Class<?> contextKlass = ClassLoader.getSystemClassLoader() + .loadClass("java.security.AccessControlContext"); + return lookup.findStatic(Subject.class, + "getSubject", MethodType.methodType(Subject.class, contextKlass)); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static MethodHandle lookupGetContext() { + try { + // Use reflection to work with Java versions that have and don't have + // AccessController. + Class<?> controllerKlass = ClassLoader.getSystemClassLoader() + .loadClass("java.security.AccessController"); + Class<?> contextKlass = ClassLoader.getSystemClassLoader() + .loadClass("java.security.AccessControlContext"); + + MethodHandles.Lookup lookup = MethodHandles.lookup(); + return lookup.findStatic( + controllerKlass, "getContext", MethodType.methodType(contextKlass)); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + /** + * Map to Subject.callAs() if available, otherwise maps to Subject.doAs(). + * + * @param subject the subject this action runs as + * @param action the action to run + * @return the result of the action + * @param <T> the type of the result + * @throws NullPointerException if action is null + * @throws CompletionException if {@code action.call()} throws an exception. + * The cause of the {@code CompletionException} is set to the exception + * thrown by {@code action.call()}. + */ + @SuppressWarnings("unchecked") + public static <T> T callAs(Subject subject, Callable<T> action) throws CompletionException { + Objects.requireNonNull(action); + if (HAS_CALL_AS) { + try { + return (T) CALL_AS.invoke(subject, action); + } catch (Throwable t) { + throw sneakyThrow(t); + } + } else { + try { + return doAs(subject, callableToPrivilegedAction(action)); + } catch (Exception e) { + throw new CompletionException(e); + } + } + } + + /** + * Map action to a Callable on Java 18 onwards, and delegates to callAs(). + * Call Subject.doAs directly on older JVM. + * <p> + * Note: Exception propagation behavior is different since Java 12, it always + * throw the original exception thrown by action; for lower Java versions, + * throw a PrivilegedActionException that wraps the original exception when + * action throw a checked exception. + * + * @param subject the subject this action runs as + * @param action the action to run + * @return the result of the action + * @param <T> the type of the result + * @throws NullPointerException if action is null + */ + @SuppressWarnings("unchecked") + public static <T> T doAs(Subject subject, PrivilegedAction<T> action) { + Objects.requireNonNull(action); + if (HAS_CALL_AS) { + try { + return callAs(subject, privilegedActionToCallable(action)); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause != null) { + throw sneakyThrow(cause); + } else { + // This should never happen, CompletionException thrown by Subject.callAs + // should always wrap an exception + throw ce; + } + } + } else { + try { + return (T) DO_AS.invoke(subject, action); + } catch (Throwable t) { + throw sneakyThrow(t); + } + } + } + + /** + * Maps action to a Callable on Java 18 onwards, and delegates to callAs(). + * Call Subject.doAs directly on older JVM. + * + * @param subject the subject this action runs as + * @param action the action to run + * @return the result of the action + * @param <T> the type of the result + * @throws NullPointerException if action is null + * @throws PrivilegedActionException if {@code action.run()} throws an checked exception. + * The cause of the {@code PrivilegedActionException} is set to the exception thrown + * by {@code action.run()}. + */ + @SuppressWarnings("unchecked") + public static <T> T doAs( + Subject subject, PrivilegedExceptionAction<T> action) throws PrivilegedActionException { + Objects.requireNonNull(action); + if (HAS_CALL_AS) { + try { + return callAs(subject, privilegedExceptionActionToCallable(action)); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Exception) { + throw new PrivilegedActionException((Exception) cause); + } else { + // This should never happen, CompletionException should only wraps an exception + throw sneakyThrow(cause); + } + } + } else { + try { + return (T) DO_AS_THROW_EXCEPTION.invoke(subject, action); + } catch (Throwable t) { + throw sneakyThrow(t); + } + } + } + + /** + * Maps to Subject.current() if available, otherwise maps to Subject.getSubject(). + * + * @return the current subject + */ + public static Subject current() { + try { + return (Subject) CURRENT.invoke(); + } catch (Throwable t) { + throw sneakyThrow(t); + } + } + + private static <T> PrivilegedAction<T> callableToPrivilegedAction( + Callable<T> callable) { + return () -> { + try { + return callable.call(); + } catch (Exception e) { + throw sneakyThrow(e); + } + }; + } + + private static <T> Callable<T> privilegedExceptionActionToCallable( + PrivilegedExceptionAction<T> action) { + return action::run; + } + + private static <T> Callable<T> privilegedActionToCallable( + PrivilegedAction<T> action) { + return action::run; + } + + /** + * The sneaky throw concept allows the caller to throw any checked exception without + * defining it explicitly in the method signature. + * <p> + * See <a href="https://www.baeldung.com/java-sneaky-throws">"Sneaky Throws" in Java</a> + * for more details. + * + * @param e the exception that will be thrown. + * @return unreachable, the method always throws an exception before returning + * @param <E> the thrown exception type, trick the compiler into inferring it as + * a {@code RuntimeException} type. + * @throws E the original exception passes by caller + */ + @SuppressWarnings("unchecked") + static <E extends Throwable> RuntimeException sneakyThrow(Throwable e) throws E { + throw (E) e; + } + + private SubjectUtil() { + } +} diff --git a/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSubjectUtil.java b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSubjectUtil.java new file mode 100644 index 00000000000..403a328d44d --- /dev/null +++ b/hadoop-common-project/hadoop-auth/src/test/java/org/apache/hadoop/security/authentication/util/TestSubjectUtil.java @@ -0,0 +1,336 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.security.authentication.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +public class TestSubjectUtil { + + // "1.8"->8, "9"->9, "10"->10 + private static final int JAVA_SPEC_VER = Math.max(8, Integer.parseInt( + System.getProperty("java.specification.version").split("\\.")[0])); + + @Test + public void testHasCallAs() { + Assert.assertEquals(JAVA_SPEC_VER > 17, SubjectUtil.HAS_CALL_AS); + } + + @Test + public void testDoAsPrivilegedActionExceptionPropagation() { + // in Java 12 onwards, always throw the original exception thrown by action; + // in lower Java versions, throw a PrivilegedActionException that wraps the + // original exception when action throws a checked exception + Throwable e = Assert.assertThrows(Exception.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedAction<Object>() { + @Override + public Object run() { + RuntimeException innerE = new RuntimeException("Inner Dummy RuntimeException"); + throw SubjectUtil.sneakyThrow(new IOException("Dummy IOException", innerE)); + } + }) + ); + if (JAVA_SPEC_VER > 11) { + Assert.assertTrue(e instanceof IOException); + Assert.assertEquals("Dummy IOException", e.getMessage()); + Assert.assertTrue(e.getCause() instanceof RuntimeException); + Assert.assertEquals("Inner Dummy RuntimeException", e.getCause().getMessage()); + Assert.assertNull(e.getCause().getCause()); + } else { + Assert.assertTrue(e instanceof PrivilegedActionException); + Assert.assertNull(e.getMessage()); + Assert.assertTrue(e.getCause() instanceof IOException); + Assert.assertEquals("Dummy IOException", e.getCause().getMessage()); + Assert.assertTrue(e.getCause().getCause() instanceof RuntimeException); + Assert.assertEquals("Inner Dummy RuntimeException", e.getCause().getCause().getMessage()); + Assert.assertNull(e.getCause().getCause().getCause()); + } + + // same as above case because PrivilegedActionException is a checked exception + e = Assert.assertThrows(PrivilegedActionException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedAction<Object>() { + @Override + public Object run() { + throw SubjectUtil.sneakyThrow(new PrivilegedActionException(null)); + } + }) + ); + if (JAVA_SPEC_VER > 11) { + Assert.assertTrue(e instanceof PrivilegedActionException); + Assert.assertNull(e.getMessage()); + Assert.assertNull(e.getCause()); + } else { + Assert.assertTrue(e instanceof PrivilegedActionException); + Assert.assertNull(e.getMessage()); + Assert.assertTrue(e.getCause() instanceof PrivilegedActionException); + Assert.assertNull(e.getCause().getMessage()); + Assert.assertNull(e.getCause().getCause()); + } + + // throw a PrivilegedActionException that wraps the original exception when action throws + // a runtime exception + e = Assert.assertThrows(RuntimeException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedAction<Object>() { + @Override + public Object run() { + throw new RuntimeException("Dummy RuntimeException"); + } + }) + ); + Assert.assertTrue(e instanceof RuntimeException); + Assert.assertEquals("Dummy RuntimeException", e.getMessage()); + Assert.assertNull(e.getCause()); + + // same as above case because CompletionException is a runtime exception + e = Assert.assertThrows(CompletionException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedAction<Object>() { + @Override + public Object run() { + throw new CompletionException("Dummy CompletionException", null); + } + }) + ); + Assert.assertTrue(e instanceof CompletionException); + Assert.assertEquals("Dummy CompletionException", e.getMessage()); + Assert.assertNull(e.getCause()); + + // throw the original error when action throws an error + e = Assert.assertThrows(LinkageError.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedAction<Object>() { + @Override + public Object run() { + throw new LinkageError("Dummy LinkageError"); + } + }) + ); + Assert.assertTrue(e instanceof LinkageError); + Assert.assertEquals("Dummy LinkageError", e.getMessage()); + Assert.assertNull(e.getCause()); + + // throw NPE when action is NULL + Assert.assertThrows(NullPointerException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), (PrivilegedAction<Object>) null) + ); + } + + @Test + public void testDoAsPrivilegedExceptionActionExceptionPropagation() { + // throw PrivilegedActionException that wraps the original exception when action throws + // a checked exception + Throwable e = Assert.assertThrows(PrivilegedActionException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedExceptionAction<Object>() { + @Override + public Object run() throws Exception { + RuntimeException innerE = new RuntimeException("Inner Dummy RuntimeException"); + throw new IOException("Dummy IOException", innerE); + } + }) + ); + Assert.assertTrue(e instanceof PrivilegedActionException); + Assert.assertNull(e.getMessage()); + Assert.assertTrue(e.getCause() instanceof IOException); + Assert.assertEquals("Dummy IOException", e.getCause().getMessage()); + Assert.assertTrue(e.getCause().getCause() instanceof RuntimeException); + Assert.assertEquals("Inner Dummy RuntimeException", e.getCause().getCause().getMessage()); + Assert.assertNull(e.getCause().getCause().getCause()); + + // same as above because PrivilegedActionException is a checked exception + e = Assert.assertThrows(PrivilegedActionException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedExceptionAction<Object>() { + @Override + public Object run() throws Exception { + throw new PrivilegedActionException(null); + } + }) + ); + Assert.assertTrue(e instanceof PrivilegedActionException); + Assert.assertNull(e.getMessage()); + Assert.assertTrue(e.getCause() instanceof PrivilegedActionException); + Assert.assertNull(e.getCause().getMessage()); + Assert.assertNull(e.getCause().getCause()); + + // throw the original exception when action throw a runtime exception + e = Assert.assertThrows(RuntimeException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedExceptionAction<Object>() { + @Override + public Object run() throws Exception { + throw new RuntimeException("Dummy RuntimeException"); + } + }) + ); + Assert.assertTrue(e instanceof RuntimeException); + Assert.assertEquals("Dummy RuntimeException", e.getMessage()); + Assert.assertNull(e.getCause()); + + // same as above case because CompletionException is a runtime exception + e = Assert.assertThrows(CompletionException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedExceptionAction<Object>() { + @Override + public Object run() throws Exception { + throw new CompletionException(null); + } + }) + ); + Assert.assertTrue(e instanceof CompletionException); + Assert.assertNull(e.getMessage()); + Assert.assertNull(e.getCause()); + + // throw the original error when action throw an error + e = Assert.assertThrows(LinkageError.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), new PrivilegedExceptionAction<Object>() { + @Override + public Object run() throws Exception { + throw new LinkageError("Dummy LinkageError"); + } + }) + ); + Assert.assertTrue(e instanceof LinkageError); + Assert.assertEquals("Dummy LinkageError", e.getMessage()); + Assert.assertNull(e.getCause()); + + // throw NPE when action is NULL + Assert.assertThrows(NullPointerException.class, () -> + SubjectUtil.doAs(SubjectUtil.current(), (PrivilegedExceptionAction<Object>) null) + ); + } + + @Test + public void testCallAsExceptionPropagation() { + // always throw a CompletionException that wraps the original exception, when action throw + // a checked or runtime exception + Throwable e = Assert.assertThrows(CompletionException.class, () -> + SubjectUtil.callAs(SubjectUtil.current(), new Callable<Object>() { + @Override + public Object call() throws Exception { + RuntimeException innerE = new RuntimeException("Inner Dummy RuntimeException"); + throw new IOException("Dummy IOException", innerE); + } + }) + ); + Assert.assertTrue(e instanceof CompletionException); + if (JAVA_SPEC_VER > 11) { + Assert.assertEquals("java.io.IOException: Dummy IOException", e.getMessage()); + Assert.assertTrue(e.getCause() instanceof IOException); + Assert.assertEquals("Dummy IOException", e.getCause().getMessage()); + Assert.assertTrue(e.getCause().getCause() instanceof RuntimeException); + Assert.assertEquals("Inner Dummy RuntimeException", e.getCause().getCause().getMessage()); + Assert.assertNull(e.getCause().getCause().getCause()); + } else { + Assert.assertEquals( + "java.security.PrivilegedActionException: java.io.IOException: Dummy IOException", + e.getMessage()); + Assert.assertTrue(e.getCause() instanceof PrivilegedActionException); + Assert.assertNull(e.getCause().getMessage()); + Assert.assertTrue(e.getCause().getCause() instanceof IOException); + Assert.assertEquals("Dummy IOException", e.getCause().getCause().getMessage()); + Assert.assertTrue(e.getCause().getCause().getCause() instanceof RuntimeException); + Assert.assertEquals("Inner Dummy RuntimeException", + e.getCause().getCause().getCause().getMessage()); + Assert.assertNull(e.getCause().getCause().getCause().getCause()); + } + + e = Assert.assertThrows(CompletionException.class, () -> + SubjectUtil.callAs(SubjectUtil.current(), new Callable<Object>() { + @Override + public Object call() throws Exception { + throw new PrivilegedActionException(null); + } + }) + ); + Assert.assertTrue(e instanceof CompletionException); + if (JAVA_SPEC_VER > 11) { + Assert.assertEquals("java.security.PrivilegedActionException", e.getMessage()); + Assert.assertTrue(e.getCause() instanceof PrivilegedActionException); + Assert.assertNull(e.getCause().getMessage()); + Assert.assertNull(e.getCause().getCause()); + } else { + Assert.assertEquals( + "java.security.PrivilegedActionException: java.security.PrivilegedActionException", + e.getMessage()); + Assert.assertTrue(e.getCause() instanceof PrivilegedActionException); + Assert.assertNull(e.getCause().getMessage()); + Assert.assertTrue(e.getCause().getCause() instanceof PrivilegedActionException); + Assert.assertNull(e.getCause().getCause().getMessage()); + Assert.assertNull(e.getCause().getCause().getCause()); + } + + e = Assert.assertThrows(CompletionException.class, () -> + SubjectUtil.callAs(SubjectUtil.current(), new Callable<Object>() { + @Override + public Object call() throws Exception { + throw new RuntimeException("Dummy RuntimeException"); + } + }) + ); + Assert.assertTrue(e instanceof CompletionException); + Assert.assertEquals("java.lang.RuntimeException: Dummy RuntimeException", e.getMessage()); + Assert.assertTrue(e.getCause() instanceof RuntimeException); + Assert.assertEquals("Dummy RuntimeException", e.getCause().getMessage()); + Assert.assertNull(e.getCause().getCause()); + + e = Assert.assertThrows(CompletionException.class, () -> + SubjectUtil.callAs(SubjectUtil.current(), new Callable<Object>() { + @Override + public Object call() throws Exception { + throw new CompletionException(null); + } + }) + ); + Assert.assertTrue(e instanceof CompletionException); + Assert.assertEquals("java.util.concurrent.CompletionException", e.getMessage()); + Assert.assertTrue(e.getCause() instanceof CompletionException); + Assert.assertNull(e.getCause().getMessage()); + + // throw original error when action throw an error + e = Assert.assertThrows(LinkageError.class, () -> + SubjectUtil.callAs(SubjectUtil.current(), new Callable<Object>() { + @Override + public Object call() throws Exception { + throw new LinkageError("Dummy LinkageError"); + } + }) + ); + Assert.assertTrue(e instanceof LinkageError); + Assert.assertEquals("Dummy LinkageError", e.getMessage()); + Assert.assertNull(e.getCause()); + + // throw NPE when action is NULL + Assert.assertThrows(NullPointerException.class, () -> + SubjectUtil.callAs(SubjectUtil.current(), null) + ); + } + + @Test + public void testSneakyThrow() { + IOException e = Assert.assertThrows(IOException.class, this::throwCheckedException); + Assert.assertEquals("Dummy IOException", e.getMessage()); + } + + // A method that throw a checked exception, but has no exception declaration in signature + private void throwCheckedException() { + throw SubjectUtil.sneakyThrow(new IOException("Dummy IOException")); + } +} diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java index 2ff747fc556..89943dc180c 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java @@ -33,8 +33,6 @@ import java.io.File; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; -import java.security.AccessControlContext; -import java.security.AccessController; import java.security.Principal; import java.security.PrivilegedAction; import java.security.PrivilegedActionException; @@ -85,6 +83,7 @@ import org.apache.hadoop.metrics2.lib.MutableRate; import org.apache.hadoop.security.SaslRpcServer.AuthMethod; import org.apache.hadoop.security.authentication.util.KerberosUtil; +import org.apache.hadoop.security.authentication.util.SubjectUtil; import org.apache.hadoop.security.token.Token; import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.util.Shell; @@ -584,8 +583,7 @@ public boolean hasKerberosCredentials() { @InterfaceStability.Evolving public static UserGroupInformation getCurrentUser() throws IOException { ensureInitialized(); - AccessControlContext context = AccessController.getContext(); - Subject subject = Subject.getSubject(context); + Subject subject = SubjectUtil.current(); if (subject == null || subject.getPrincipals(User.class).isEmpty()) { return getLoginUser(); } else { @@ -1927,9 +1925,9 @@ public <T> T doAs(PrivilegedAction<T> action) { LOG.debug("PrivilegedAction [as: {}][action: {}]", this, action, new Exception()); } - return Subject.doAs(subject, action); + return SubjectUtil.doAs(subject, action); } - + /** * Run the given action as the user, potentially throwing an exception. * @param <T> the return type of the run method @@ -1950,7 +1948,7 @@ public <T> T doAs(PrivilegedExceptionAction<T> action LOG.debug("PrivilegedAction [as: {}][action: {}]", this, action, new Exception()); } - return Subject.doAs(subject, action); + return SubjectUtil.doAs(subject, action); } catch (PrivilegedActionException pae) { Throwable cause = pae.getCause(); LOG.debug("PrivilegedActionException as: {}", this, cause); --------------------------------------------------------------------- To unsubscribe, e-mail: common-commits-unsubscr...@hadoop.apache.org For additional commands, e-mail: common-commits-h...@hadoop.apache.org