This is an automated email from the ASF dual-hosted git repository.
stevel pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/hadoop.git
The following commit(s) were added to refs/heads/trunk by this push:
new e57306eb2af HADOOP-19668. Add SubjectInheritingThread and update
Daemon to restore pre-JDK22 Subject behaviour (#8061)
e57306eb2af is described below
commit e57306eb2af466bd17d31217a097942be7636554
Author: Istvan Toth <[email protected]>
AuthorDate: Wed Nov 12 15:54:05 2025 +0100
HADOOP-19668. Add SubjectInheritingThread and update Daemon to restore
pre-JDK22 Subject behaviour (#8061)
This is the first part of HADOOP-19574. "Restore Subject propagation
semantics for Java 22+"
Add SubjectInheritingThread and update Daemon, but only update the classes
using Daemon
Contributed by Istvan Toth
---
.../security/authentication/util/SubjectUtil.java | 101 ++++++++++
.../java/org/apache/hadoop/ha/HealthMonitor.java | 2 +-
.../apache/hadoop/io/retry/AsyncCallHandler.java | 2 +-
.../main/java/org/apache/hadoop/util/Daemon.java | 70 ++++++-
.../util/concurrent/SubjectInheritingThread.java | 212 +++++++++++++++++++++
.../util/concurrent/TestSubjectPropagation.java | 186 ++++++++++++++++++
.../java/org/apache/hadoop/hdfs/DataStreamer.java | 4 +-
.../org/apache/hadoop/hdfs/DeadNodeDetector.java | 2 +-
.../apache/hadoop/hdfs/LocatedBlocksRefresher.java | 2 +-
.../hadoop/hdfs/nfs/nfs3/OpenFileCtxCache.java | 2 +-
.../hdfs/server/blockmanagement/BlockManager.java | 2 +-
.../hadoop/hdfs/server/namenode/Checkpointer.java | 2 +-
.../fsdataset/impl/TestSpaceReservation.java | 2 +-
13 files changed, 570 insertions(+), 19 deletions(-)
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
index faf2d6c7d81..e364f040596 100644
---
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
@@ -56,6 +56,18 @@ public final class SubjectUtil {
HAS_CALL_AS ? null : lookupDoAsThrowException();
private static final MethodHandle CURRENT = lookupCurrent();
+ // copied from org.apache.hadoop.util.Shell to break circular dependency
+ // "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]));
+
+ public static final boolean THREAD_INHERITS_SUBJECT =
checkThreadInheritsSubject();
+
+ /**
+ * Try to return the method handle for Subject#callAs()
+ *
+ * @return the method handle, or null if the Java version does not have it
+ */
private static MethodHandle lookupCallAs() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
@@ -71,6 +83,38 @@ private static MethodHandle lookupCallAs() {
}
}
+ /**
+ * Determine whether we need to explicitly propagate the Subject into new
Threads.
+ *
+ * @return true if new Threads inherit the Subject from the parent
+ */
+ private static boolean checkThreadInheritsSubject() {
+
+ boolean securityManagerEnabled = true;
+ try {
+ // TODO this needs SecurityManager to compile, use reflection to look it
up instead
+ SecurityManager sm = System.getSecurityManager();
+ System.setSecurityManager(sm);
+ } catch (UnsupportedOperationException e) {
+ // JDK24+ unconditionally throws this, so we don't need to check for
JDK24+
+ // explicitly
+ securityManagerEnabled = false;
+ } catch (Throwable t) {
+ // don't care
+ }
+
+ return JAVA_SPEC_VER < 22 || securityManagerEnabled;
+ }
+
+ /**
+ * Look up the method handle for Subject#doAs(PrivilegedAction)
+ *
+ * This is only called if Subject#callAs() does not exist.
+ * If we can't fall back to doAs(), that's a hard error.
+ *
+ * @return the method handle
+ * @throws ExceptionInInitializerError if unable to get the method handle
+ */
private static MethodHandle lookupDoAs() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
@@ -82,6 +126,15 @@ private static MethodHandle lookupDoAs() {
}
}
+ /**
+ * Look up the method handle for Subject#doAs(PrivilegedExceptionAction)
+ *
+ * This is only called if Subject#callAs() does not exist.
+ * If we can't fall back to doAs(), that's a hard error.
+ *
+ * @return the method handle
+ * @throws ExceptionInInitializerError if unable to get the method handle
+ */
private static MethodHandle lookupDoAsThrowException() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
@@ -93,6 +146,15 @@ private static MethodHandle lookupDoAsThrowException() {
}
}
+ /**
+ * Look up the method handle for Subject#current().
+ *
+ * If Subject#current() is not present, fall back to returning
+ * a method handle for Subject.getSubject(AccessController.getContext())
+ *
+ * @return the method handle or null if it does not exist
+ * @throws ExceptionInInitializerError if neither current() nor the fallback
is found
+ */
private static MethodHandle lookupCurrent() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
@@ -112,6 +174,15 @@ private static MethodHandle lookupCurrent() {
}
}
+ /**
+ * Look up the method handle for Subject#getSubject(AccessControlContext)
+ *
+ * This is only called if Subject#current() does not exist.
+ * If we can't fall back to getSubject(), that's a hard error.
+ *
+ * @return the method handle
+ * @throws ExceptionInInitializerError if cannot get the handle
+ */
private static MethodHandle lookupGetSubject() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
@@ -124,6 +195,15 @@ private static MethodHandle lookupGetSubject() {
}
}
+ /**
+ * Look up the method handle for AccessController.getAccessControlContext()
+ *
+ * This is only called if Subject#current() does not exist.
+ * If we can't find this method, then we can't fall back which is hard error.
+ *
+ * @return the method handle
+ * @throws ExceptionInInitializerError if cannot get the handle
+ */
private static MethodHandle lookupGetContext() {
try {
// Use reflection to work with Java versions that have and don't have
@@ -264,6 +344,13 @@ public static Subject current() {
}
}
+ /**
+ * Convert a Callable into a PrivilegedAction
+ *
+ * @param <T> return type
+ * @param callable to be converted
+ * @return PrivilegedAction wrapping the callable
+ */
private static <T> PrivilegedAction<T> callableToPrivilegedAction(
Callable<T> callable) {
return () -> {
@@ -275,11 +362,25 @@ private static <T> PrivilegedAction<T>
callableToPrivilegedAction(
};
}
+ /**
+ * Convert a PrivilegedExceptionAction into a Callable
+ *
+ * @param <T> return type
+ * @param action to be wrapped
+ * @return Callable wrapping the action
+ */
private static <T> Callable<T> privilegedExceptionActionToCallable(
PrivilegedExceptionAction<T> action) {
return action::run;
}
+ /**
+ * Convert a PrivilegedAction into a Callable
+ *
+ * @param <T> return type
+ * @param action to be wrapped
+ * @return Callable wrapping the action
+ */
private static <T> Callable<T> privilegedActionToCallable(
PrivilegedAction<T> action) {
return action::run;
diff --git
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/HealthMonitor.java
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/HealthMonitor.java
index d222d52e373..d37c321ff43 100644
---
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/HealthMonitor.java
+++
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ha/HealthMonitor.java
@@ -283,7 +283,7 @@ public void uncaughtException(Thread t, Throwable e) {
}
@Override
- public void run() {
+ public void work() {
while (shouldRun) {
try {
loopUntilConnected();
diff --git
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/retry/AsyncCallHandler.java
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/retry/AsyncCallHandler.java
index 60210ccd920..010f4928be9 100644
---
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/retry/AsyncCallHandler.java
+++
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/retry/AsyncCallHandler.java
@@ -158,7 +158,7 @@ void tryStart() {
if (running.compareAndSet(null, current)) {
final Daemon daemon = new Daemon() {
@Override
- public void run() {
+ public void work() {
for (; isRunning(this);) {
final long waitTime = checkCalls();
tryStop(this);
diff --git
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Daemon.java
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Daemon.java
index f735b82e428..88ac215e4c3 100644
---
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Daemon.java
+++
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/Daemon.java
@@ -18,25 +18,74 @@
package org.apache.hadoop.util;
+import java.security.PrivilegedAction;
import java.util.concurrent.ThreadFactory;
+import javax.security.auth.Subject;
+
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
-/** A thread that has called {@link Thread#setDaemon(boolean) } with true.*/
[email protected]({"HDFS", "MapReduce"})
+/**
+ * A thread that has called {@link Thread#setDaemon(boolean) } with true.
+ * <p>
+ * The runnable code must either be specified in the runnable parameter or in
+ * the overridden work() method.
+ * <p>
+ * See {@link org.apache.hadoop.util.concurrent.SubjectInheritingThread} for
the Subject inheritance behavior this
+ * class adds.
+ *
+ */
[email protected]({ "HDFS", "MapReduce" })
@InterfaceStability.Unstable
public class Daemon extends Thread {
+ Subject startSubject;
+
+ @Override
+ public final void start() {
+ if (!SubjectUtil.THREAD_INHERITS_SUBJECT) {
+ startSubject = SubjectUtil.current();
+ }
+ super.start();
+ }
+
+ /**
+ * Override this instead of run()
+ */
+ public void work() {
+ if (runnable != null) {
+ runnable.run();
+ }
+ }
+
+ @Override
+ public final void run() {
+ if (!SubjectUtil.THREAD_INHERITS_SUBJECT) {
+ SubjectUtil.doAs(startSubject, new PrivilegedAction<Void>() {
+
+ @Override
+ public Void run() {
+ work();
+ return null;
+ }
+
+ });
+ } else {
+ work();
+ }
+ }
+
{
- setDaemon(true); // always a daemon
+ setDaemon(true); // always a daemon
}
/**
- * Provide a factory for named daemon threads,
- * for use in ExecutorServices constructors
+ * Provide a factory for named daemon threads, for use in ExecutorServices
+ * constructors
*/
- @InterfaceAudience.LimitedPrivate({"HDFS", "MapReduce"})
+ @InterfaceAudience.LimitedPrivate({ "HDFS", "MapReduce" })
public static class DaemonFactory extends Daemon implements ThreadFactory {
@Override
@@ -47,6 +96,7 @@ public Thread newThread(Runnable runnable) {
}
Runnable runnable = null;
+
/** Construct a daemon thread. */
public Daemon() {
super();
@@ -54,23 +104,25 @@ public Daemon() {
/**
* Construct a daemon thread.
+ *
* @param runnable runnable.
*/
public Daemon(Runnable runnable) {
super(runnable);
this.runnable = runnable;
- this.setName(((Object)runnable).toString());
+ this.setName(((Object) runnable).toString());
}
/**
* Construct a daemon thread to be part of a specified thread group.
- * @param group thread group.
+ *
+ * @param group thread group.
* @param runnable runnable.
*/
public Daemon(ThreadGroup group, Runnable runnable) {
super(group, runnable);
this.runnable = runnable;
- this.setName(((Object)runnable).toString());
+ this.setName(((Object) runnable).toString());
}
public Runnable getRunnable() {
diff --git
a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/concurrent/SubjectInheritingThread.java
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/concurrent/SubjectInheritingThread.java
new file mode 100644
index 00000000000..e9b6745340d
--- /dev/null
+++
b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/util/concurrent/SubjectInheritingThread.java
@@ -0,0 +1,212 @@
+/**
+ * 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.concurrent;
+
+import java.security.PrivilegedAction;
+import javax.security.auth.Subject;
+
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
+
+/**
+ * Helper class to restore Subject propagation behavior of threads after the
+ * JEP411/JEP486 changes.
+ * <p>
+ * Java propagates the current Subject to any new Threads in all version up to
+ * Java 21. In Java 22-23 the Subject is only propagated if the SecurityManager
+ * is enabled, while in Java 24+ it is never propagated.
+ * <p>
+ * Hadoop security heavily relies on the original behavior, as Subject is at
the
+ * core of JAAS. This class wraps thread. It overrides start() and saves the
+ * Subject of the current thread, and wraps the payload in a
+ * Subject.doAs()/callAs() call to restore it in the newly created Thread.
+ * <p>
+ * When specifying a Runnable, this class is used in exactly the same way as
+ * Thread.
+ * <p>
+ * {@link #run()} cannot be directly overridden, as that would also override
the
+ * subject restoration logic. SubjectInheritingThread provides a {@link work()}
+ * method instead, which is wrapped and invoked by its own final {@link run()}
+ * method.
+ */
+public class SubjectInheritingThread extends Thread {
+
+ private Subject startSubject;
+ // {@link Thread#target} is private, so we need our own
+ private Runnable hadoopTarget;
+
+ /**
+ * Behaves similarly to {@link Thread#Thread()} constructor, but the code to
run
+ * must be specified by overriding the {@link #work()} instead of the {link
+ * #run()} method.
+ */
+ public SubjectInheritingThread() {
+ super();
+ }
+
+ /**
+ * Behaves similarly to {@link Thread#Thread(Runnable)} constructor.
+ *
+ * @param target the object whose {@code run} method is invoked when this
thread
+ * is started. If {@code null}, this classes {@code run} method
+ * does nothing.
+ */
+ public SubjectInheritingThread(Runnable target) {
+ super();
+ this.hadoopTarget = target;
+ }
+
+ /**
+ * Behaves similarly to {@link Thread#Thread(ThreadGroup, Runnable)}
+ * constructor.
+ *
+ * @param group the thread group. If {@code null} and there is a security
+ * manager, the group is determined by
+ * {@linkplain SecurityManager#getThreadGroup
+ * SecurityManager.getThreadGroup()}. If there is not a
security
+ * manager or {@code
+ * SecurityManager.getThreadGroup()} returns {@code null}, the group
is
+ * set to the current thread's thread group.
+ *
+ * @param target the object whose {@code run} method is invoked when this
thread
+ * is started. If {@code null}, this thread's run method is
+ * invoked.
+ * @throws SecurityException if the current thread cannot create a thread in
the
+ * specified thread group
+ */
+ public SubjectInheritingThread(ThreadGroup group, Runnable target) {
+ // The target passed to Thread has no effect, we only pass it
+ // because there is no super(group) constructor.
+ super(group, target);
+ this.hadoopTarget = target;
+ }
+
+ /**
+ * Behaves similarly to {@link Thread#Thread(Runnable, String)} constructor.
+ *
+ * @param target the object whose {@code run} method is invoked when this
thread
+ * is started. If {@code null}, this thread's run method is
+ * invoked.
+ *
+ * @param name the name of the new thread
+ *
+ * @throws SecurityException if the current thread cannot create a thread in
the
+ * specified thread group
+ */
+ public SubjectInheritingThread(Runnable target, String name) {
+ super(name);
+ this.hadoopTarget = target;
+ }
+
+ /**
+ * Behaves similarly to {@link Thread#Thread(String)} constructor.
+ *
+ * @param name the name of the new thread
+ */
+ public SubjectInheritingThread(String name) {
+ super(name);
+ }
+
+ /**
+ * Behaves similarly to {@link Thread#Thread(ThreadGroup, String)}
constructor.
+ *
+ * @param group the thread group. If {@code null} and there is a security
+ * manager, the group is determined by
+ * {@linkplain SecurityManager#getThreadGroup
+ * SecurityManager.getThreadGroup()}. If there is not a security
+ * manager or {@code
+ * SecurityManager.getThreadGroup()} returns {@code null}, the group
is
+ * set to the current thread's thread group.
+ *
+ * @param name the name of the new thread
+ */
+ public SubjectInheritingThread(ThreadGroup group, String name) {
+ super(group, name);
+ }
+
+ /**
+ * Behaves similarly to {@link Thread#Thread(ThreadGroup, Runnable, String)}
+ * constructor.
+ *
+ * @param group the thread group. If {@code null} and there is a security
+ * manager, the group is determined by
+ * {@linkplain SecurityManager#getThreadGroup
+ * SecurityManager.getThreadGroup()}. If there is not a
security
+ * manager or {@code
+ * SecurityManager.getThreadGroup()} returns {@code null}, the group
is
+ * set to the current thread's thread group.
+ *
+ * @param target the object whose {@code run} method is invoked when this
thread
+ * is started. If {@code null}, this thread's run method is
+ * invoked.
+ *
+ * @param name the name of the new thread
+ *
+ * @throws SecurityException if the current thread cannot create a thread in
the
+ * specified thread group or cannot override the
+ * context class loader methods.
+ */
+ public SubjectInheritingThread(ThreadGroup group, Runnable target, String
name) {
+ super(group, name);
+ this.hadoopTarget = target;
+ }
+
+ /**
+ * Behaves similarly to pre-Java 22 {@link Thread#start()}. It saves the
current
+ * Subject before starting the new thread, which is then used as the Subject
for
+ * the Runnable or the overridden work() method.
+ */
+ @Override
+ public final void start() {
+ if (!SubjectUtil.THREAD_INHERITS_SUBJECT) {
+ startSubject = SubjectUtil.current();
+ }
+ super.start();
+ }
+
+ /**
+ * This is the equivalent of {@link Thread#run()}. Override this instead of
+ * {@link #run()} Subject will be propagated like in pre-Java 22 Thread.
+ */
+ public void work() {
+ if (hadoopTarget != null) {
+ hadoopTarget.run();
+ }
+ }
+
+ /**
+ * This cannot be overridden in this class. Override the {@link #work()}
method
+ * instead which behaves like pre-Java 22 {@link Thread#run()}
+ */
+ @Override
+ public final void run() {
+ if (!SubjectUtil.THREAD_INHERITS_SUBJECT) {
+ SubjectUtil.doAs(startSubject, new PrivilegedAction<Void>() {
+
+ @Override
+ public Void run() {
+ work();
+ return null;
+ }
+
+ });
+ } else {
+ work();
+ }
+ }
+}
diff --git
a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/concurrent/TestSubjectPropagation.java
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/concurrent/TestSubjectPropagation.java
new file mode 100644
index 00000000000..018a91cdfc2
--- /dev/null
+++
b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/util/concurrent/TestSubjectPropagation.java
@@ -0,0 +1,186 @@
+/**
+ * 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.concurrent;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
+import java.util.concurrent.Callable;
+
+import javax.security.auth.Subject;
+
+import org.apache.hadoop.security.authentication.util.SubjectUtil;
+import org.apache.hadoop.util.Daemon;
+import org.apache.hadoop.util.Shell;
+import org.junit.jupiter.api.Test;
+
+public class TestSubjectPropagation {
+
+ private Subject childSubject = null;
+
+ @Test
+ public void testSubjectInheritingThreadOverride() {
+ Subject parentSubject = new Subject();
+ childSubject = null;
+
+ SubjectUtil.callAs(parentSubject, new Callable<Void>() {
+ public Void call() throws InterruptedException {
+ SubjectInheritingThread t = new SubjectInheritingThread() {
+ @Override
+ public void work() {
+ childSubject = SubjectUtil.current();
+ }
+ };
+ t.start();
+ t.join(1000);
+ return (Void) null;
+ }
+ });
+
+ assertEquals(parentSubject, childSubject);
+ }
+
+ @Test
+ public void testSubjectInheritingThreadRunnable() {
+ Subject parentSubject = new Subject();
+ childSubject = null;
+
+ SubjectUtil.callAs(parentSubject, new Callable<Void>() {
+ public Void call() throws InterruptedException {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ childSubject = SubjectUtil.current();
+ }
+ };
+
+ SubjectInheritingThread t = new SubjectInheritingThread(r);
+ t.start();
+ t.join(1000);
+ return (Void) null;
+ }
+ });
+
+ assertEquals(parentSubject, childSubject);
+ }
+
+ @Test
+ public void testDaemonOverride() {
+ Subject parentSubject = new Subject();
+ childSubject = null;
+
+ SubjectUtil.callAs(parentSubject, new Callable<Void>() {
+ public Void call() throws InterruptedException {
+ Daemon t = new Daemon() {
+ @Override
+ public void work() {
+ childSubject = SubjectUtil.current();
+ }
+ };
+ t.start();
+ t.join(1000);
+ return (Void) null;
+ }
+ });
+
+ assertEquals(parentSubject, childSubject);
+ }
+
+ @Test
+ public void testDaemonRunnable() {
+ Subject parentSubject = new Subject();
+ childSubject = null;
+
+ SubjectUtil.callAs(parentSubject, new Callable<Void>() {
+ public Void call() throws InterruptedException {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ childSubject = SubjectUtil.current();
+ }
+ };
+
+ Daemon t = new Daemon(r);
+ t.start();
+ t.join(1000);
+ return (Void) null;
+ }
+ });
+
+ assertEquals(parentSubject, childSubject);
+ }
+
+ @Test
+ public void testThreadOverride() {
+ Subject parentSubject = new Subject();
+ childSubject = null;
+
+ SubjectUtil.callAs(parentSubject, new Callable<Void>() {
+ public Void call() throws InterruptedException {
+
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ childSubject = SubjectUtil.current();
+ }
+ };
+ t.start();
+ t.join(1000);
+ return (Void) null;
+ }
+ });
+
+ if (SubjectUtil.THREAD_INHERITS_SUBJECT) {
+ assertEquals(parentSubject, childSubject);
+ } else {
+ // This is the behaviour that breaks Hadoop authorization
+ assertNull(childSubject);
+ }
+ }
+
+ @Test
+ public void testThreadRunnable() {
+ Subject parentSubject = new Subject();
+ childSubject = null;
+
+ SubjectUtil.callAs(parentSubject, new Callable<Void>() {
+ public Void call() throws InterruptedException {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ childSubject = SubjectUtil.current();
+ }
+ };
+
+ Thread t = new Thread(r);
+ t.start();
+ t.join(1000);
+ return (Void) null;
+ }
+ });
+
+ if (SubjectUtil.THREAD_INHERITS_SUBJECT) {
+ assertEquals(parentSubject, childSubject);
+ } else {
+ // This is the behaviour that breaks Hadoop authorization
+ assertNull(childSubject);
+ }
+ }
+
+}
diff --git
a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DataStreamer.java
b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DataStreamer.java
index 8d13640eadb..7caa88d6d65 100644
---
a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DataStreamer.java
+++
b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DataStreamer.java
@@ -693,7 +693,7 @@ private boolean shouldStop() {
* and closes them. Any error recovery is also done by this thread.
*/
@Override
- public void run() {
+ public void work() {
TraceScope scope = null;
while (!streamerClosed && dfsClient.clientRunning) {
// if the Responder encountered an error, shutdown Responder
@@ -1167,7 +1167,7 @@ private class ResponseProcessor extends Daemon {
}
@Override
- public void run() {
+ public void work() {
setName("ResponseProcessor for block " + block);
PipelineAck ack = new PipelineAck();
diff --git
a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java
b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java
index 465497ffb9b..0dc1b1686b9 100644
---
a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java
+++
b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DeadNodeDetector.java
@@ -250,7 +250,7 @@ public DeadNodeDetector(String name, Configuration conf) {
}
@Override
- public void run() {
+ public void work() {
while (!Thread.currentThread().isInterrupted()) {
clearAndGetDetectedDeadNodes();
LOG.debug("Current detector state {}, the detected nodes: {}.", state,
diff --git
a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/LocatedBlocksRefresher.java
b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/LocatedBlocksRefresher.java
index 454d1f9cd93..be8fd94247a 100644
---
a/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/LocatedBlocksRefresher.java
+++
b/hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/LocatedBlocksRefresher.java
@@ -99,7 +99,7 @@ public Thread newThread(Runnable r) {
}
@Override
- public void run() {
+ public void work() {
while (!Thread.currentThread().isInterrupted()) {
if (!waitForInterval()) {
diff --git
a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/OpenFileCtxCache.java
b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/OpenFileCtxCache.java
index 70ae4b29e9f..591776e2090 100644
---
a/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/OpenFileCtxCache.java
+++
b/hadoop-hdfs-project/hadoop-hdfs-nfs/src/main/java/org/apache/hadoop/hdfs/nfs/nfs3/OpenFileCtxCache.java
@@ -246,7 +246,7 @@ void shouldRun(boolean shouldRun) {
}
@Override
- public void run() {
+ public void work() {
while (shouldRun) {
scan(streamTimeout);
diff --git
a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java
b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java
index d1f02c47e90..ed29578ee0c 100644
---
a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java
+++
b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/blockmanagement/BlockManager.java
@@ -3960,7 +3960,7 @@ public void processMisReplicatedBlocks() {
reconstructionQueuesInitializer = new Daemon() {
@Override
- public void run() {
+ public void work() {
try {
processMisReplicatesAsync();
} catch (InterruptedException ie) {
diff --git
a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/Checkpointer.java
b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/Checkpointer.java
index 29b262598bf..fb63a13fb33 100644
---
a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/Checkpointer.java
+++
b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/Checkpointer.java
@@ -128,7 +128,7 @@ void shutdown() {
// The main work loop
//
@Override
- public void run() {
+ public void work() {
// How often to check the size of the edit log (min of
checkpointCheckPeriod and checkpointPeriod)
long periodMSec = checkpointConf.getCheckPeriod() * 1000;
// How often to checkpoint regardless of number of txns
diff --git
a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestSpaceReservation.java
b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestSpaceReservation.java
index ef84c1732d5..bbb3c055235 100644
---
a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestSpaceReservation.java
+++
b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/fsdataset/impl/TestSpaceReservation.java
@@ -552,7 +552,7 @@ private static class Writer extends Daemon {
}
@Override
- public void run() {
+ public void work() {
/**
* Create a file, write up to 3 blocks of data and close the file.
* Do this in a loop until we are told to stop.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]