[ https://issues.apache.org/jira/browse/HADOOP-19212?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18015178#comment-18015178 ]
ASF GitHub Bot commented on HADOOP-19212: ----------------------------------------- steveloughran commented on code in PR #7886: URL: https://github.com/apache/hadoop/pull/7886#discussion_r2288121282 ########## hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/SubjectUtil.java: ########## @@ -0,0 +1,230 @@ +/** + * 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.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +@Private +public class SubjectUtil { + private static MethodHandle CALL_AS; Review Comment: thes should be final, as the static sections are trying to set them up as such. This may need some tuning of that code so that the compiler is happy there's only one attempt ever made to set it. ########## hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/SubjectUtil.java: ########## @@ -0,0 +1,230 @@ +/** + * 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.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +@Private +public class SubjectUtil { + private static MethodHandle CALL_AS; + private static MethodHandle CURRENT; + + static { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + try { + // Subject.doAs() is deprecated for removal and replaced by Subject.callAs(). + // Lookup first the new API, since for Java versions where both exist, the + // new API delegates to the old API (e.g. Java 18, 19 and 20). + // Otherwise (e.g. Java 17), lookup the old API. + CALL_AS = lookup.findStatic(Subject.class, "callAs", + MethodType.methodType(Object.class, Subject.class, Callable.class)); + } catch (NoSuchMethodException x) { + try { + // Lookup the old API. + MethodType oldSignature = MethodType.methodType( + Object.class, Subject.class, PrivilegedExceptionAction.class); + MethodHandle doAs = lookup.findStatic(Subject.class, "doAs", oldSignature); + // Convert the Callable used in the new API to the PrivilegedAction used + // in the old API. + MethodType convertSignature = MethodType.methodType( + PrivilegedExceptionAction.class, Callable.class); + MethodHandle converter = lookup.findStatic( + SubjectUtil.class, "callableToPrivilegedExceptionAction", convertSignature); + CALL_AS = MethodHandles.filterArguments(doAs, 1, converter); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + static { + 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. + CURRENT = lookup.findStatic( + Subject.class, "current", MethodType.methodType(Subject.class)); + } catch (NoSuchMethodException e) { + MethodHandle getContext = lookupGetContext(); + MethodHandle getSubject = lookupGetSubject(); + CURRENT = MethodHandles.filterReturnValue(getContext, getSubject); + } catch (IllegalAccessException e) { + throw new AssertionError(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 AssertionError(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 AssertionError(e); + } + } + + /** + * Maps to Subject.callAs() if available, otherwise maps to Subject.doAs(). + * It also wraps the Callable so that the Subject is propagated to the new thread + * in all Java versions. + * + * @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 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 { Review Comment: add test, verify that * callable throwing a PrivilegedActionException is mapped to completion exception * callable raising any RTE is handled correctly ########## hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/SubjectUtil.java: ########## @@ -0,0 +1,230 @@ +/** + * 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.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +@Private +public class SubjectUtil { + private static MethodHandle CALL_AS; + private static MethodHandle CURRENT; + + static { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + try { + // Subject.doAs() is deprecated for removal and replaced by Subject.callAs(). + // Lookup first the new API, since for Java versions where both exist, the + // new API delegates to the old API (e.g. Java 18, 19 and 20). + // Otherwise (e.g. Java 17), lookup the old API. + CALL_AS = lookup.findStatic(Subject.class, "callAs", + MethodType.methodType(Object.class, Subject.class, Callable.class)); + } catch (NoSuchMethodException x) { + try { + // Lookup the old API. + MethodType oldSignature = MethodType.methodType( + Object.class, Subject.class, PrivilegedExceptionAction.class); + MethodHandle doAs = lookup.findStatic(Subject.class, "doAs", oldSignature); + // Convert the Callable used in the new API to the PrivilegedAction used + // in the old API. + MethodType convertSignature = MethodType.methodType( + PrivilegedExceptionAction.class, Callable.class); + MethodHandle converter = lookup.findStatic( + SubjectUtil.class, "callableToPrivilegedExceptionAction", convertSignature); + CALL_AS = MethodHandles.filterArguments(doAs, 1, converter); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + static { + 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. + CURRENT = lookup.findStatic( + Subject.class, "current", MethodType.methodType(Subject.class)); + } catch (NoSuchMethodException e) { + MethodHandle getContext = lookupGetContext(); + MethodHandle getSubject = lookupGetSubject(); + CURRENT = MethodHandles.filterReturnValue(getContext, getSubject); + } catch (IllegalAccessException e) { + throw new AssertionError(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 AssertionError(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 AssertionError(e); + } + } + + /** + * Maps to Subject.callAs() if available, otherwise maps to Subject.doAs(). + * It also wraps the Callable so that the Subject is propagated to the new thread + * in all Java versions. + * + * @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 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 { + try { + return (T) CALL_AS.invoke(subject, action); + } catch (PrivilegedActionException e) { + throw new CompletionException(e.getCause()); + } catch (Throwable t) { + throw sneakyThrow(t); + } + } + + /** + * Maps action to a Callable, and delegates to callAs(). On older JVMs, the + * action may be double wrapped (into Callable, and then back into + * PrivilegedAction). + * + * @param subject the subject this action runs as + * @param action action the action to run + * @return the result of the action + * @param <T> the type of the result + */ + public static <T> T doAs(Subject subject, PrivilegedAction<T> action) { + try { + return callAs(subject, privilegedActionToCallable(action)); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause != null) { + throw sneakyThrow(cause); + } else { + // This should never happen, as CompletionException should always wrap an exception + throw ce; + } + } + } + + /** + * Maps action to a Callable, and delegates to callAs(). On older JVMs, the + * action may be double wrapped (into Callable, and then back into PrivilegedAction). + * + * @param subject the subject this action runs as + * @param action action the action to run + * @return the result of the action + * @param <T> the type of the result + * @throws PrivilegedActionException if {@code action.run()} throws an exception. + * The cause of the {@code PrivilegedActionException} is set to the exception + * thrown by {@code action.run()}. + */ + public static <T> T doAs( + Subject subject, PrivilegedExceptionAction<T> action) throws PrivilegedActionException { + try { + return callAs(subject, privilegedExceptionActionToCallable(action)); + } catch (CompletionException ce) { + try { + Exception cause = (Exception) ce.getCause(); + throw new PrivilegedActionException(cause); + } catch (ClassCastException castException) { + // This should never happen, as PrivilegedExceptionAction should not wrap + // non-checked exceptions + throw new PrivilegedActionException(new UndeclaredThrowableException(ce.getCause())); + } + } + } + + /** + * 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); + } + } + + @SuppressWarnings("unused") + private static <T> PrivilegedExceptionAction<T> callableToPrivilegedExceptionAction( + Callable<T> callable) { + return callable::call; + } + + private static <T> Callable<T> privilegedExceptionActionToCallable( + PrivilegedExceptionAction<T> action) { + return action::run; + } + + private static <T> Callable<T> privilegedActionToCallable( + PrivilegedAction<T> action) { + return action::run; + } + + @SuppressWarnings("unchecked") + private static <E extends Throwable> RuntimeException sneakyThrow(Throwable e) throws E { Review Comment: add javadoc -it is a use of templates I've not seen before...having the type of raised exception inferred from the context around the invocation ########## hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/SubjectUtil.java: ########## @@ -0,0 +1,230 @@ +/** + * 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.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +@Private +public class SubjectUtil { + private static MethodHandle CALL_AS; + private static MethodHandle CURRENT; + + static { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + try { + // Subject.doAs() is deprecated for removal and replaced by Subject.callAs(). + // Lookup first the new API, since for Java versions where both exist, the + // new API delegates to the old API (e.g. Java 18, 19 and 20). + // Otherwise (e.g. Java 17), lookup the old API. + CALL_AS = lookup.findStatic(Subject.class, "callAs", + MethodType.methodType(Object.class, Subject.class, Callable.class)); + } catch (NoSuchMethodException x) { + try { + // Lookup the old API. + MethodType oldSignature = MethodType.methodType( + Object.class, Subject.class, PrivilegedExceptionAction.class); + MethodHandle doAs = lookup.findStatic(Subject.class, "doAs", oldSignature); + // Convert the Callable used in the new API to the PrivilegedAction used + // in the old API. + MethodType convertSignature = MethodType.methodType( + PrivilegedExceptionAction.class, Callable.class); + MethodHandle converter = lookup.findStatic( + SubjectUtil.class, "callableToPrivilegedExceptionAction", convertSignature); + CALL_AS = MethodHandles.filterArguments(doAs, 1, converter); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + static { + 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. + CURRENT = lookup.findStatic( + Subject.class, "current", MethodType.methodType(Subject.class)); + } catch (NoSuchMethodException e) { + MethodHandle getContext = lookupGetContext(); + MethodHandle getSubject = lookupGetSubject(); + CURRENT = MethodHandles.filterReturnValue(getContext, getSubject); + } catch (IllegalAccessException e) { + throw new AssertionError(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 AssertionError(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 AssertionError(e); + } + } + + /** + * Maps to Subject.callAs() if available, otherwise maps to Subject.doAs(). + * It also wraps the Callable so that the Subject is propagated to the new thread + * in all Java versions. + * + * @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 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 { + try { + return (T) CALL_AS.invoke(subject, action); + } catch (PrivilegedActionException e) { + throw new CompletionException(e.getCause()); + } catch (Throwable t) { + throw sneakyThrow(t); + } + } + + /** + * Maps action to a Callable, and delegates to callAs(). On older JVMs, the + * action may be double wrapped (into Callable, and then back into + * PrivilegedAction). + * + * @param subject the subject this action runs as + * @param action action the action to run + * @return the result of the action + * @param <T> the type of the result + */ + public static <T> T doAs(Subject subject, PrivilegedAction<T> action) { + try { + return callAs(subject, privilegedActionToCallable(action)); + } catch (CompletionException ce) { + Throwable cause = ce.getCause(); + if (cause != null) { + throw sneakyThrow(cause); + } else { + // This should never happen, as CompletionException should always wrap an exception Review Comment: unless the invoked callable raises it ########## hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/SubjectUtil.java: ########## @@ -0,0 +1,230 @@ +/** + * 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.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +@Private +public class SubjectUtil { + private static MethodHandle CALL_AS; + private static MethodHandle CURRENT; + + static { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + try { + // Subject.doAs() is deprecated for removal and replaced by Subject.callAs(). + // Lookup first the new API, since for Java versions where both exist, the + // new API delegates to the old API (e.g. Java 18, 19 and 20). + // Otherwise (e.g. Java 17), lookup the old API. + CALL_AS = lookup.findStatic(Subject.class, "callAs", + MethodType.methodType(Object.class, Subject.class, Callable.class)); + } catch (NoSuchMethodException x) { + try { + // Lookup the old API. + MethodType oldSignature = MethodType.methodType( + Object.class, Subject.class, PrivilegedExceptionAction.class); + MethodHandle doAs = lookup.findStatic(Subject.class, "doAs", oldSignature); + // Convert the Callable used in the new API to the PrivilegedAction used + // in the old API. + MethodType convertSignature = MethodType.methodType( + PrivilegedExceptionAction.class, Callable.class); + MethodHandle converter = lookup.findStatic( + SubjectUtil.class, "callableToPrivilegedExceptionAction", convertSignature); + CALL_AS = MethodHandles.filterArguments(doAs, 1, converter); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + static { + 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. + CURRENT = lookup.findStatic( + Subject.class, "current", MethodType.methodType(Subject.class)); + } catch (NoSuchMethodException e) { + MethodHandle getContext = lookupGetContext(); + MethodHandle getSubject = lookupGetSubject(); + CURRENT = MethodHandles.filterReturnValue(getContext, getSubject); + } catch (IllegalAccessException e) { + throw new AssertionError(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 AssertionError(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 AssertionError(e); + } + } + + /** + * Maps to Subject.callAs() if available, otherwise maps to Subject.doAs(). + * It also wraps the Callable so that the Subject is propagated to the new thread + * in all Java versions. + * + * @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 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 { + try { + return (T) CALL_AS.invoke(subject, action); + } catch (PrivilegedActionException e) { + throw new CompletionException(e.getCause()); + } catch (Throwable t) { + throw sneakyThrow(t); + } + } + + /** + * Maps action to a Callable, and delegates to callAs(). On older JVMs, the + * action may be double wrapped (into Callable, and then back into + * PrivilegedAction). + * + * @param subject the subject this action runs as + * @param action action the action to run + * @return the result of the action + * @param <T> the type of the result + */ + public static <T> T doAs(Subject subject, PrivilegedAction<T> action) { Review Comment: tests to verify handling. what happens if the callable raises a CompletionException without an inner cause? that "should never happen" path is reached -so verify ########## hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/util/SubjectUtil.java: ########## @@ -0,0 +1,230 @@ +/** + * 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.util; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; + +import javax.security.auth.Subject; + +import org.apache.hadoop.classification.InterfaceAudience.Private; + +@Private +public class SubjectUtil { Review Comment: add javadocs. I think there should also be some boolean indicating whether the new or old methods were picked up. This can be accompanied by a unit test which verifies that on java > 17 the new ones are found. > [JDK23] org.apache.hadoop.security.UserGroupInformation use of Subject needs > to move to replacement APIs > -------------------------------------------------------------------------------------------------------- > > Key: HADOOP-19212 > URL: https://issues.apache.org/jira/browse/HADOOP-19212 > Project: Hadoop Common > Issue Type: Sub-task > Components: security > Affects Versions: 3.5.0 > Reporter: Alan Bateman > Priority: Major > Labels: pull-request-available > > `javax.security.auth.Subject.getSubject` and `Subject.doAs` were deprecated > for removal in JDK 17. The replacement APIs are `Subject.current` and > `callAs`. See [JEP 411]([https://openjdk.org/jeps/411]) for background. > The `Subject.getSubject` API has been "degraded" in JDK 23 to throw > `UnsupportedOperationException` if not running with the option to allow a > SecurityManager. In a future JDK release, the `Subject.getSubject` API will > be degraded further to throw`UnsupportedOperationException` unconditionally. > [renaissance/issues/439]([https://github.com/renaissance-benchmarks/renaissance/issues/439]) > is a failure with a Spark benchmark due to the code in > `org.apache.hadoop.security.UserGroupInformation` using the deprecated > `Subject.getSubject` method. The maintainers of this code need to migrate > this code to the replacement APIs to ensure that this code will continue to > work once the security manager feature is removed. -- This message was sent by Atlassian Jira (v8.20.10#820010) --------------------------------------------------------------------- To unsubscribe, e-mail: common-issues-unsubscr...@hadoop.apache.org For additional commands, e-mail: common-issues-h...@hadoop.apache.org