001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.List;
023
024/**
025 * <p>
026 * Helpers for {@code java.lang.Thread} and {@code java.lang.ThreadGroup}.
027 * </p>
028 * <p>
029 * #ThreadSafe#
030 * </p>
031 *
032 * @see java.lang.Thread
033 * @see java.lang.ThreadGroup
034 * @since 3.5
035 */
036public class ThreadUtils {
037
038    /**
039     * Return the active thread with the specified id if it belongs to the specified thread group.
040     *
041     * @param threadId The thread id
042     * @param threadGroup The thread group
043     * @return The thread which belongs to a specified thread group and the thread's id match the specified id.
044     * {@code null} is returned if no such thread exists
045     * @throws IllegalArgumentException if the specified id is zero or negative or the group is null
046     * @throws  SecurityException
047     *          if the current thread cannot access the system thread group
048     *
049     * @throws  SecurityException  if the current thread cannot modify
050     *          thread groups from this thread's thread group up to the system thread group
051     */
052    public static Thread findThreadById(final long threadId, final ThreadGroup threadGroup) {
053        Validate.isTrue(threadGroup != null, "The thread group must not be null");
054        final Thread thread = findThreadById(threadId);
055        if(thread != null && threadGroup.equals(thread.getThreadGroup())) {
056            return thread;
057        }
058        return null;
059    }
060
061    /**
062     * Return the active thread with the specified id if it belongs to a thread group with the specified group name.
063     *
064     * @param threadId The thread id
065     * @param threadGroupName The thread group name
066     * @return The threads which belongs to a thread group with the specified group name and the thread's id match the specified id.
067     * {@code null} is returned if no such thread exists
068     * @throws IllegalArgumentException if the specified id is zero or negative or the group name is null
069     * @throws  SecurityException
070     *          if the current thread cannot access the system thread group
071     *
072     * @throws  SecurityException  if the current thread cannot modify
073     *          thread groups from this thread's thread group up to the system thread group
074     */
075    public static Thread findThreadById(final long threadId, final String threadGroupName) {
076        Validate.isTrue(threadGroupName != null, "The thread group name must not be null");
077        final Thread thread = findThreadById(threadId);
078        if(thread != null && thread.getThreadGroup() != null && thread.getThreadGroup().getName().equals(threadGroupName)) {
079            return thread;
080        }
081        return null;
082    }
083
084    /**
085     * Return active threads with the specified name if they belong to a specified thread group.
086     *
087     * @param threadName The thread name
088     * @param threadGroup The thread group
089     * @return The threads which belongs to a thread group and the thread's name match the specified name,
090     * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
091     * @throws IllegalArgumentException if the specified thread name or group is null
092     * @throws  SecurityException
093     *          if the current thread cannot access the system thread group
094     *
095     * @throws  SecurityException  if the current thread cannot modify
096     *          thread groups from this thread's thread group up to the system thread group
097     */
098    public static Collection<Thread> findThreadsByName(final String threadName, final ThreadGroup threadGroup) {
099        return findThreads(threadGroup, false, new NamePredicate(threadName));
100    }
101
102    /**
103     * Return active threads with the specified name if they belong to a thread group with the specified group name.
104     *
105     * @param threadName The thread name
106     * @param threadGroupName The thread group name
107     * @return The threads which belongs to a thread group with the specified group name and the thread's name match the specified name,
108     * An empty collection is returned if no such thread exists. The collection returned is always unmodifiable.
109     * @throws IllegalArgumentException if the specified thread name or group name is null
110     * @throws  SecurityException
111     *          if the current thread cannot access the system thread group
112     *
113     * @throws  SecurityException  if the current thread cannot modify
114     *          thread groups from this thread's thread group up to the system thread group
115     */
116    public static Collection<Thread> findThreadsByName(final String threadName, final String threadGroupName) {
117        Validate.isTrue(threadName != null, "The thread name must not be null");
118        Validate.isTrue(threadGroupName != null, "The thread group name must not be null");
119
120        final Collection<ThreadGroup> threadGroups = findThreadGroups(new NamePredicate(threadGroupName));
121
122        if(threadGroups.isEmpty()) {
123            return Collections.emptyList();
124        }
125
126        final Collection<Thread> result = new ArrayList<>();
127        final NamePredicate threadNamePredicate = new NamePredicate(threadName);
128        for(final ThreadGroup group : threadGroups) {
129            result.addAll(findThreads(group, false, threadNamePredicate));
130        }
131        return Collections.unmodifiableCollection(result);
132    }
133
134    /**
135     * Return active thread groups with the specified group name.
136     *
137     * @param threadGroupName The thread group name
138     * @return the thread groups with the specified group name or an empty collection if no such thread group exists. The collection returned is always unmodifiable.
139     * @throws IllegalArgumentException if group name is null
140     * @throws  SecurityException
141     *          if the current thread cannot access the system thread group
142     *
143     * @throws  SecurityException  if the current thread cannot modify
144     *          thread groups from this thread's thread group up to the system thread group
145     */
146    public static Collection<ThreadGroup> findThreadGroupsByName(final String threadGroupName) {
147        return findThreadGroups(new NamePredicate(threadGroupName));
148    }
149
150    /**
151     * Return all active thread groups excluding the system thread group (A thread group is active if it has been not destroyed).
152     *
153     * @return all thread groups excluding the system thread group. The collection returned is always unmodifiable.
154     * @throws  SecurityException
155     *          if the current thread cannot access the system thread group
156     *
157     * @throws  SecurityException  if the current thread cannot modify
158     *          thread groups from this thread's thread group up to the system thread group
159     */
160    public static Collection<ThreadGroup> getAllThreadGroups() {
161        return findThreadGroups(ALWAYS_TRUE_PREDICATE);
162    }
163
164    /**
165     * Return the system thread group (sometimes also referred as "root thread group").
166     *
167     * @return the system thread group
168     * @throws  SecurityException  if the current thread cannot modify
169     *          thread groups from this thread's thread group up to the system thread group
170     */
171    public static ThreadGroup getSystemThreadGroup() {
172        ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
173        while(threadGroup.getParent() != null) {
174            threadGroup = threadGroup.getParent();
175        }
176        return threadGroup;
177    }
178
179    /**
180     * Return all active threads (A thread is active if it has been started and has not yet died).
181     *
182     * @return all active threads. The collection returned is always unmodifiable.
183     * @throws  SecurityException
184     *          if the current thread cannot access the system thread group
185     *
186     * @throws  SecurityException  if the current thread cannot modify
187     *          thread groups from this thread's thread group up to the system thread group
188     */
189    public static Collection<Thread> getAllThreads() {
190        return findThreads(ALWAYS_TRUE_PREDICATE);
191    }
192
193    /**
194     * Return active threads with the specified name.
195     *
196     * @param threadName The thread name
197     * @return The threads with the specified name or an empty collection if no such thread exists. The collection returned is always unmodifiable.
198     * @throws IllegalArgumentException if the specified name is null
199     * @throws  SecurityException
200     *          if the current thread cannot access the system thread group
201     *
202     * @throws  SecurityException  if the current thread cannot modify
203     *          thread groups from this thread's thread group up to the system thread group
204     */
205    public static Collection<Thread> findThreadsByName(final String threadName) {
206        return findThreads(new NamePredicate(threadName));
207    }
208
209    /**
210     * Return the active thread with the specified id.
211     *
212     * @param threadId The thread id
213     * @return The thread with the specified id or {@code null} if no such thread exists
214     * @throws IllegalArgumentException if the specified id is zero or negative
215     * @throws  SecurityException
216     *          if the current thread cannot access the system thread group
217     *
218     * @throws  SecurityException  if the current thread cannot modify
219     *          thread groups from this thread's thread group up to the system thread group
220     */
221    public static Thread findThreadById(final long threadId) {
222        final Collection<Thread> result = findThreads(new ThreadIdPredicate(threadId));
223        return result.isEmpty() ? null : result.iterator().next();
224    }
225
226    /**
227     * <p>
228     * ThreadUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
229     * {@code ThreadUtils.getAllThreads()}
230     * </p>
231     * <p>
232     * This constructor is public to permit tools that require a JavaBean instance to operate.
233     * </p>
234     */
235    public ThreadUtils() {
236        super();
237    }
238
239    /**
240     * A predicate for selecting threads.
241     */
242    //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate
243    public interface ThreadPredicate /*extends java.util.function.Predicate<Thread>*/{
244
245        /**
246         * Evaluates this predicate on the given thread.
247         * @param thread the thread
248         * @return {@code true} if the thread matches the predicate, otherwise {@code false}
249         */
250        boolean test(Thread thread);
251    }
252
253    /**
254     * A predicate for selecting threadgroups.
255     */
256    //if java minimal version for lang becomes 1.8 extend this interface from java.util.function.Predicate
257    public interface ThreadGroupPredicate /*extends java.util.function.Predicate<ThreadGroup>*/{
258
259        /**
260         * Evaluates this predicate on the given threadgroup.
261         * @param threadGroup the threadgroup
262         * @return {@code true} if the threadGroup matches the predicate, otherwise {@code false}
263         */
264        boolean test(ThreadGroup threadGroup);
265    }
266
267    /**
268     * Predicate which always returns true.
269     */
270    public static final AlwaysTruePredicate ALWAYS_TRUE_PREDICATE = new AlwaysTruePredicate();
271
272    /**
273     * A predicate implementation which always returns true.
274     */
275    private static final class AlwaysTruePredicate implements ThreadPredicate, ThreadGroupPredicate{
276
277        private AlwaysTruePredicate() {
278        }
279
280        @Override
281        public boolean test(final ThreadGroup threadGroup) {
282            return true;
283        }
284
285        @Override
286        public boolean test(final Thread thread) {
287            return true;
288        }
289    }
290
291    /**
292     * A predicate implementation which matches a thread or threadgroup name.
293     */
294    public static class NamePredicate implements ThreadPredicate, ThreadGroupPredicate {
295
296        private final String name;
297
298        /**
299         * Predicate constructor
300         *
301         * @param name thread or threadgroup name
302         * @throws IllegalArgumentException if the name is {@code null}
303         */
304        public NamePredicate(final String name) {
305            super();
306            Validate.isTrue(name != null, "The name must not be null");
307            this.name = name;
308        }
309
310        @Override
311        public boolean test(final ThreadGroup threadGroup) {
312            return threadGroup != null && threadGroup.getName().equals(name);
313        }
314
315        @Override
316        public boolean test(final Thread thread) {
317            return thread != null && thread.getName().equals(name);
318        }
319    }
320
321    /**
322     * A predicate implementation which matches a thread id.
323     */
324    public static class ThreadIdPredicate implements ThreadPredicate {
325
326        private final long threadId;
327
328        /**
329         * Predicate constructor
330         *
331         * @param threadId the threadId to match
332         * @throws IllegalArgumentException if the threadId is zero or negative
333         */
334        public ThreadIdPredicate(final long threadId) {
335            super();
336            if (threadId <= 0) {
337                throw new IllegalArgumentException("The thread id must be greater than zero");
338            }
339            this.threadId = threadId;
340        }
341
342        @Override
343        public boolean test(final Thread thread) {
344            return thread != null && thread.getId() == threadId;
345        }
346    }
347
348    /**
349     * Select all active threads which match the given predicate.
350     *
351     * @param predicate the predicate
352     * @return An unmodifiable {@code Collection} of active threads matching the given predicate
353     *
354     * @throws IllegalArgumentException if the predicate is null
355     * @throws  SecurityException
356     *          if the current thread cannot access the system thread group
357     * @throws  SecurityException  if the current thread cannot modify
358     *          thread groups from this thread's thread group up to the system thread group
359     */
360    public static Collection<Thread> findThreads(final ThreadPredicate predicate){
361        return findThreads(getSystemThreadGroup(), true, predicate);
362    }
363
364    /**
365     * Select all active threadgroups which match the given predicate.
366     *
367     * @param predicate the predicate
368     * @return An unmodifiable {@code Collection} of active threadgroups matching the given predicate
369     * @throws IllegalArgumentException if the predicate is null
370     * @throws  SecurityException
371     *          if the current thread cannot access the system thread group
372     * @throws  SecurityException  if the current thread cannot modify
373     *          thread groups from this thread's thread group up to the system thread group
374     */
375    public static Collection<ThreadGroup> findThreadGroups(final ThreadGroupPredicate predicate){
376        return findThreadGroups(getSystemThreadGroup(), true, predicate);
377    }
378
379    /**
380     * Select all active threads which match the given predicate and which belongs to the given thread group (or one of its subgroups).
381     *
382     * @param group the thread group
383     * @param recurse if {@code true} then evaluate the predicate recursively on all threads in all subgroups of the given group
384     * @param predicate the predicate
385     * @return An unmodifiable {@code Collection} of active threads which match the given predicate and which belongs to the given thread group
386     * @throws IllegalArgumentException if the given group or predicate is null
387     * @throws  SecurityException  if the current thread cannot modify
388     *          thread groups from this thread's thread group up to the system thread group
389     */
390    public static Collection<Thread> findThreads(final ThreadGroup group, final boolean recurse, final ThreadPredicate predicate) {
391        Validate.isTrue(group != null, "The group must not be null");
392        Validate.isTrue(predicate != null, "The predicate must not be null");
393
394        int count = group.activeCount();
395        Thread[] threads;
396        do {
397            threads = new Thread[count + (count / 2) + 1]; //slightly grow the array size
398            count = group.enumerate(threads, recurse);
399            //return value of enumerate() must be strictly less than the array size according to javadoc
400        } while (count >= threads.length);
401
402        final List<Thread> result = new ArrayList<>(count);
403        for (int i = 0; i < count; ++i) {
404            if (predicate.test(threads[i])) {
405                result.add(threads[i]);
406            }
407        }
408        return Collections.unmodifiableCollection(result);
409    }
410
411    /**
412     * Select all active threadgroups which match the given predicate and which is a subgroup of the given thread group (or one of its subgroups).
413     *
414     * @param group the thread group
415     * @param recurse if {@code true} then evaluate the predicate recursively on all threadgroups in all subgroups of the given group
416     * @param predicate the predicate
417     * @return An unmodifiable {@code Collection} of active threadgroups which match the given predicate and which is a subgroup of the given thread group
418     * @throws IllegalArgumentException if the given group or predicate is null
419     * @throws  SecurityException  if the current thread cannot modify
420     *          thread groups from this thread's thread group up to the system thread group
421     */
422    public static Collection<ThreadGroup> findThreadGroups(final ThreadGroup group, final boolean recurse, final ThreadGroupPredicate predicate){
423        Validate.isTrue(group != null, "The group must not be null");
424        Validate.isTrue(predicate != null, "The predicate must not be null");
425
426        int count = group.activeGroupCount();
427        ThreadGroup[] threadGroups;
428        do {
429            threadGroups = new ThreadGroup[count + (count / 2) + 1]; //slightly grow the array size
430            count = group.enumerate(threadGroups, recurse);
431            //return value of enumerate() must be strictly less than the array size according to javadoc
432        } while(count >= threadGroups.length);
433
434        final List<ThreadGroup> result = new ArrayList<>(count);
435        for(int i = 0; i < count; ++i) {
436            if(predicate.test(threadGroups[i])) {
437                result.add(threadGroups[i]);
438            }
439        }
440        return Collections.unmodifiableCollection(result);
441    }
442}