Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x
This commit is contained in:
commit
2b259354f2
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
|
||||
//
|
||||
// This program and the accompanying materials are made available under the
|
||||
// terms of the Eclipse Public License v. 2.0 which is available at
|
||||
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
|
||||
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
|
||||
//
|
||||
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
|
||||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.util.thread;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Convenience class to ensure that a new Thread is created
|
||||
* inside a privileged block.
|
||||
*
|
||||
* This prevents the Thread constructor
|
||||
* from pinning the caller's context classloader. This happens
|
||||
* when the Thread constructor takes a snapshot of the current
|
||||
* calling context - which contains ProtectionDomains that may
|
||||
* reference the context classloader - and remembers it for the
|
||||
* lifetime of the Thread.
|
||||
*/
|
||||
class PrivilegedThreadFactory
|
||||
{
|
||||
/**
|
||||
* Use a Supplier to make a new thread, calling it within
|
||||
* a privileged block to prevent classloader pinning.
|
||||
*
|
||||
* @param newThreadSupplier a Supplier to create a fresh thread
|
||||
* @return a new thread, protected from classloader pinning.
|
||||
*/
|
||||
static <T extends Thread> T newThread(Supplier<T> newThreadSupplier)
|
||||
{
|
||||
return AccessController.doPrivileged(new PrivilegedAction<T>()
|
||||
{
|
||||
@Override
|
||||
public T run()
|
||||
{
|
||||
return newThreadSupplier.get();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@ package org.eclipse.jetty.util.thread;
|
|||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -697,11 +699,15 @@ public class QueuedThreadPool extends ContainerLifeCycle implements ThreadFactor
|
|||
@Override
|
||||
public Thread newThread(Runnable runnable)
|
||||
{
|
||||
Thread thread = new Thread(_threadGroup, runnable);
|
||||
thread.setDaemon(isDaemon());
|
||||
thread.setPriority(getThreadsPriority());
|
||||
thread.setName(_name + "-" + thread.getId());
|
||||
return thread;
|
||||
return PrivilegedThreadFactory.newThread(() ->
|
||||
{
|
||||
Thread thread = new Thread(_threadGroup, runnable);
|
||||
thread.setDaemon(isDaemon());
|
||||
thread.setPriority(getThreadsPriority());
|
||||
thread.setName(_name + "-" + thread.getId());
|
||||
thread.setContextClassLoader(getClass().getClassLoader());
|
||||
return thread;
|
||||
});
|
||||
}
|
||||
|
||||
protected void removeThread(Thread thread)
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
|
||||
package org.eclipse.jetty.util.thread;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
@ -31,7 +33,10 @@ import org.slf4j.LoggerFactory;
|
|||
public class ShutdownThread extends Thread
|
||||
{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ShutdownThread.class);
|
||||
private static final ShutdownThread _thread = new ShutdownThread();
|
||||
private static final ShutdownThread _thread = PrivilegedThreadFactory.newThread(() ->
|
||||
{
|
||||
return new ShutdownThread();
|
||||
});
|
||||
|
||||
private final AutoLock _lock = new AutoLock();
|
||||
private boolean _hooked;
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
package org.eclipse.jetty.util.thread;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -22,6 +24,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
import org.eclipse.jetty.logging.StacklessLogging;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -849,6 +852,28 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
|||
assertThat(count(dump, "QueuedThreadPoolTest.lambda$testDump$"), is(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContextClassLoader() throws Exception
|
||||
{
|
||||
QueuedThreadPool tp = new QueuedThreadPool();
|
||||
try (StacklessLogging stackless = new StacklessLogging(QueuedThreadPool.class))
|
||||
{
|
||||
//change the current thread's classloader to something else
|
||||
Thread.currentThread().setContextClassLoader(new URLClassLoader(new URL[] {}));
|
||||
|
||||
//create a new thread
|
||||
Thread t = tp.newThread(() ->
|
||||
{
|
||||
//the executing thread should be still set to the classloader of the QueuedThreadPool,
|
||||
//not that of the thread that created this thread.
|
||||
assertThat(Thread.currentThread().getContextClassLoader(), Matchers.equalTo(QueuedThreadPool.class.getClassLoader()));
|
||||
});
|
||||
|
||||
//new thread should be set to the classloader of the QueuedThreadPool
|
||||
assertThat(t.getContextClassLoader(), Matchers.equalTo(QueuedThreadPool.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
private int count(String s, String p)
|
||||
{
|
||||
int c = 0;
|
||||
|
|
Loading…
Reference in New Issue