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