1 /*
2 * JBoss, Home of Professional Open Source.
3 * Copyright 2017 Red Hat, Inc., and individual contributors
4 * as indicated by the @author tags.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.jboss.threads;
20
21 import java.lang.reflect.Field;
22 import java.util.concurrent.Executor;
23 import java.util.concurrent.ExecutorService;
24 import java.util.concurrent.ThreadPoolExecutor;
25 import java.util.concurrent.RejectedExecutionHandler;
26 import java.util.concurrent.ThreadFactory;
27 import java.util.concurrent.ScheduledExecutorService;
28 import java.security.PrivilegedAction;
29 import java.security.AccessController;
30
31 import org.jboss.logging.Logger;
32 import org.wildfly.common.Assert;
33 import sun.misc.Unsafe;
34
35 /**
36 * JBoss thread- and executor-related utility and factory methods.
37 */
38 public final class JBossExecutors {
39
40 private static final Logger THREAD_ERROR_LOGGER = Logger.getLogger("org.jboss.threads.errors");
41
42 private JBossExecutors() {}
43
44 private static final RuntimePermission COPY_CONTEXT_CLASSLOADER_PERMISSION = new RuntimePermission("copyClassLoader");
45
46 private static final ExecutorService REJECTING_EXECUTOR_SERVICE = new DelegatingExecutorService(RejectingExecutor.INSTANCE);
47 private static final ExecutorService DISCARDING_EXECUTOR_SERVICE = new DelegatingExecutorService(DiscardingExecutor.INSTANCE);
48
49 // ==================================================
50 // DIRECT EXECUTORS
51 // ==================================================
52
53 /**
54 * Get the direct executor. This executor will immediately run any task it is given, and propagate back any
55 * run-time exceptions thrown.
56 *
57 * @return the direct executor instance
58 */
59 public static Executor directExecutor() {
60 return SimpleDirectExecutor.INSTANCE;
61 }
62
63 /**
64 * Get the rejecting executor. This executor will reject any task submitted to it.
65 *
66 * @return the rejecting executor instance
67 */
68 public static Executor rejectingExecutor() {
69 return RejectingExecutor.INSTANCE;
70 }
71
72 /**
73 * Get a rejecting executor. This executor will reject any task submitted to it with the given message.
74 *
75 * @param message the reject message
76 * @return the rejecting executor instance
77 */
78 public static Executor rejectingExecutor(final String message) {
79 return new RejectingExecutor(message);
80 }
81
82 /**
83 * Get the rejecting executor service. This executor will reject any task submitted to it. It cannot be shut down.
84 *
85 * @return the rejecting executor service instance
86 */
87 public static ExecutorService rejectingExecutorService() {
88 return REJECTING_EXECUTOR_SERVICE;
89 }
90
91 /**
92 * Get the rejecting executor service. This executor will reject any task submitted to it with the given message.
93 * It cannot be shut down.
94 *
95 * @param message the reject message
96 * @return the rejecting executor service instance
97 */
98 public static ExecutorService rejectingExecutorService(final String message) {
99 return protectedExecutorService(rejectingExecutor(message));
100 }
101
102 /**
103 * Get the discarding executor. This executor will silently discard any task submitted to it.
104 *
105 * @return the discarding executor instance
106 */
107 public static Executor discardingExecutor() {
108 return DiscardingExecutor.INSTANCE;
109 }
110
111 /**
112 * Get the discarding executor service. This executor will silently discard any task submitted to it. It cannot
113 * be shut down.
114 *
115 * @return the discarding executor service instance
116 */
117 public static ExecutorService discardingExecutorService() {
118 return DISCARDING_EXECUTOR_SERVICE;
119 }
120
121 /**
122 * Create an executor which runs tasks with the given context class loader.
123 *
124 * @param delegate the executor to delegate to
125 * @param taskClassLoader the context class loader to use
126 * @return the new direct executor
127 */
128 public static Executor contextClassLoaderExecutor(final Executor delegate, final ClassLoader taskClassLoader) {
129 return new DelegatingExecutor(delegate) {
130 public void execute(final Runnable command) {
131 super.execute(new ContextClassLoaderSavingRunnable(taskClassLoader, command));
132 }
133 };
134 }
135
136 // ==================================================
137 // REJECTED EXECUTION HANDLERS
138 // ==================================================
139
140 private static final RejectedExecutionHandler ABORT_POLICY = new ThreadPoolExecutor.AbortPolicy();
141 private static final RejectedExecutionHandler CALLER_RUNS_POLICY = new ThreadPoolExecutor.CallerRunsPolicy();
142 private static final RejectedExecutionHandler DISCARD_OLDEST_POLICY = new ThreadPoolExecutor.DiscardOldestPolicy();
143 private static final RejectedExecutionHandler DISCARD_POLICY = new ThreadPoolExecutor.DiscardPolicy();
144
145 /**
146 * Get the abort policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
147 *
148 * @return the abort policy
149 * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy
150 */
151 public static RejectedExecutionHandler abortPolicy() {
152 return ABORT_POLICY;
153 }
154
155 /**
156 * Get the caller-runs policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
157 *
158 * @return the caller-runs policy
159 * @see java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
160 */
161 public static RejectedExecutionHandler callerRunsPolicy() {
162 return CALLER_RUNS_POLICY;
163 }
164
165 /**
166 * Get the discard-oldest policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
167 *
168 * @return the discard-oldest policy
169 * @see java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy
170 */
171 public static RejectedExecutionHandler discardOldestPolicy() {
172 return DISCARD_OLDEST_POLICY;
173 }
174
175 /**
176 * Get the discard policy for a {@link java.util.concurrent.ThreadPoolExecutor}.
177 *
178 * @return the discard policy
179 * @see java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
180 */
181 public static RejectedExecutionHandler discardPolicy() {
182 return DISCARD_POLICY;
183 }
184
185 /**
186 * Get a handoff policy for a {@link java.util.concurrent.ThreadPoolExecutor}. The returned instance will
187 * delegate to another executor in the event that the task is rejected.
188 *
189 * @param target the target executor
190 * @return the new handoff policy implementation
191 */
192 public static RejectedExecutionHandler handoffPolicy(final Executor target) {
193 return new HandoffRejectedExecutionHandler(target);
194 }
195
196 // ==================================================
197 // PROTECTED EXECUTOR SERVICE WRAPPERS
198 // ==================================================
199
200 /**
201 * Wrap an executor with an {@code ExecutorService} instance which supports all the features of {@code ExecutorService}
202 * except for shutting down the executor.
203 *
204 * @param target the target executor
205 * @return the executor service
206 */
207 public static ExecutorService protectedExecutorService(final Executor target) {
208 return new DelegatingExecutorService(target);
209 }
210
211 /**
212 * Wrap a scheduled executor with a {@code ScheduledExecutorService} instance which supports all the features of
213 * {@code ScheduledExecutorService} except for shutting down the executor.
214 *
215 * @param target the target executor
216 * @return the executor service
217 */
218 public static ScheduledExecutorService protectedScheduledExecutorService(final ScheduledExecutorService target) {
219 return new DelegatingScheduledExecutorService(target);
220 }
221
222 // ==================================================
223 // THREAD FACTORIES
224 // ==================================================
225
226 /**
227 * Create a thread factory which resets all thread-local storage and delegates to the given thread factory.
228 * You must have the {@link RuntimePermission}{@code ("modifyThread")} permission to use this method.
229 *
230 * @param delegate the delegate thread factory
231 * @return the resetting thread factory
232 * @throws SecurityException if the caller does not have the {@link RuntimePermission}{@code ("modifyThread")}
233 * permission
234 */
235 public static ThreadFactory resettingThreadFactory(final ThreadFactory delegate) throws SecurityException {
236 return new ThreadFactory() {
237 public Thread newThread(final Runnable r) {
238 return delegate.newThread(new ThreadLocalResettingRunnable(r));
239 }
240 };
241 }
242
243 private static final Runnable TCCL_RESETTER = new Runnable() {
244 public void run() {
245 Thread.currentThread().setContextClassLoader(null);
246 }
247
248 public String toString() {
249 return "ContextClassLoader-resetting Runnable";
250 }
251 };
252
253 // ==================================================
254 // RUNNABLES
255 // ==================================================
256
257 private static final Runnable NULL_RUNNABLE = NullRunnable.getInstance();
258
259 /**
260 * Get the null runnable which does nothing.
261 *
262 * @return the null runnable
263 */
264 public static Runnable nullRunnable() {
265 return NULL_RUNNABLE;
266 }
267
268 /**
269 * Get a {@code Runnable} which, when executed, clears the thread context class loader (if the caller has sufficient
270 * privileges).
271 *
272 * @return the runnable
273 */
274 public static Runnable contextClassLoaderResetter() {
275 return TCCL_RESETTER;
276 }
277
278 /**
279 * Create a task that delegates to the given task, preserving the context classloader which was in effect when
280 * this method was invoked.
281 *
282 * @param delegate the delegate runnable
283 * @return the wrapping runnable
284 * @throws SecurityException if a security manager exists and the caller does not have the {@code "copyClassLoader"}
285 * {@link RuntimePermission}.
286 */
287 public static Runnable classLoaderPreservingTask(final Runnable delegate) throws SecurityException {
288 final SecurityManager sm = System.getSecurityManager();
289 if (sm != null) {
290 sm.checkPermission(COPY_CONTEXT_CLASSLOADER_PERMISSION);
291 }
292 return classLoaderPreservingTaskUnchecked(delegate);
293 }
294
295 static final ClassLoader SAFE_CL;
296
297 static {
298 ClassLoader safeClassLoader = JBossExecutors.class.getClassLoader();
299 if (safeClassLoader == null) {
300 safeClassLoader = ClassLoader.getSystemClassLoader();
301 }
302 if (safeClassLoader == null) {
303 safeClassLoader = new ClassLoader() {
304 };
305 }
306 SAFE_CL = safeClassLoader;
307 }
308
309 static Runnable classLoaderPreservingTaskUnchecked(final Runnable delegate) {
310 Assert.checkNotNullParam("delegate", delegate);
311 return new ContextClassLoaderSavingRunnable(getContextClassLoader(Thread.currentThread()), delegate);
312 }
313
314 static final Unsafe unsafe;
315
316 static final long contextClassLoaderOffs;
317
318 static {
319 unsafe = AccessController.doPrivileged(new PrivilegedAction<Unsafe>() {
320 public Unsafe run() {
321 try {
322 final Field field = Unsafe.class.getDeclaredField("theUnsafe");
323 field.setAccessible(true);
324 return (Unsafe) field.get(null);
325 } catch (IllegalAccessException e) {
326 throw new IllegalAccessError(e.getMessage());
327 } catch (NoSuchFieldException e) {
328 throw new NoSuchFieldError(e.getMessage());
329 }
330 }
331 });
332 try {
333 contextClassLoaderOffs = unsafe.objectFieldOffset(Thread.class.getDeclaredField("contextClassLoader"));
334 } catch (NoSuchFieldException e) {
335 throw new NoSuchFieldError(e.getMessage());
336 }
337 }
338
339 /**
340 * Privileged method to get the context class loader of the given thread.
341 *
342 * @param thread the thread to introspect
343 * @return the context class loader
344 */
345 static ClassLoader getContextClassLoader(final Thread thread) {
346 return (ClassLoader) unsafe.getObject(thread, contextClassLoaderOffs);
347 }
348
349 /**
350 * Privileged method to get and set the context class loader of the given thread.
351 *
352 * @param thread the thread to introspect
353 * @param newClassLoader the new context class loader
354 * @return the old context class loader
355 */
356 static ClassLoader getAndSetContextClassLoader(final Thread thread, final ClassLoader newClassLoader) {
357 try {
358 return getContextClassLoader(thread);
359 } finally {
360 setContextClassLoader(thread, newClassLoader);
361 }
362 }
363
364 /**
365 * Privileged method to set the context class loader of the given thread.
366 *
367 * @param thread the thread to introspect
368 * @param classLoader the new context class loader
369 */
370 static void setContextClassLoader(final Thread thread, final ClassLoader classLoader) {
371 unsafe.putObject(thread, contextClassLoaderOffs, classLoader);
372 }
373
374 /**
375 * Privileged method to clear the context class loader of the given thread to a safe non-{@code null} value.
376 *
377 * @param thread the thread to introspect
378 */
379 static void clearContextClassLoader(final Thread thread) {
380 unsafe.putObject(thread, contextClassLoaderOffs, SAFE_CL);
381 }
382
383 // ==================================================
384 // UNCAUGHT EXCEPTION HANDLERS
385 // ==================================================
386
387 /**
388 * Get an uncaught exception handler which logs to the given logger.
389 *
390 * @param log the logger
391 * @return the handler
392 */
393 public static Thread.UncaughtExceptionHandler loggingExceptionHandler(final Logger log) {
394 return new LoggingUncaughtExceptionHandler(log);
395 }
396
397 /**
398 * Get an uncaught exception handler which logs to the given logger.
399 *
400 * @param categoryName the name of the logger category to log to
401 * @return the handler
402 */
403 public static Thread.UncaughtExceptionHandler loggingExceptionHandler(final String categoryName) {
404 return new LoggingUncaughtExceptionHandler(Logger.getLogger(categoryName));
405 }
406
407 private static final Thread.UncaughtExceptionHandler LOGGING_HANDLER = loggingExceptionHandler(THREAD_ERROR_LOGGER);
408
409 /**
410 * Get an uncaught exception handler which logs to the default error logger.
411 *
412 * @return the handler
413 */
414 public static Thread.UncaughtExceptionHandler loggingExceptionHandler() {
415 return LOGGING_HANDLER;
416 }
417 }
418