pan3793 commented on code in PR #7886:
URL: https://github.com/apache/hadoop/pull/7886#discussion_r2289902982


##########
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:
   the exception propagation mechanism is changed in Java 12 ... I refactored 
the code, and added `TestSubjectUtil` to cover all cases I can imagine.
   
   Now, the UT passed under JDK 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 
22, 23, 24, 25
   
   ```
   mvn -Dsurefire.failIfNoSpecifiedTests=false install -pl :hadoop-auth -am 
-Dtest=TestSubjectUtil
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: common-issues-unsubscr...@hadoop.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: common-issues-unsubscr...@hadoop.apache.org
For additional commands, e-mail: common-issues-h...@hadoop.apache.org

Reply via email to