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