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.wicket.util.lang;
018
019import java.lang.management.ManagementFactory;
020import java.lang.management.RuntimeMXBean;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.Map.Entry;
024
025import org.apache.wicket.util.string.interpolator.MapVariableInterpolator;
026import org.slf4j.Logger;
027
028/**
029 * A utility class for dealing with {@link Thread}s.
030 */
031public class Threads
032{
033
034        private static final String FORMAT = "\"${name}\"${isDaemon} prio=${priority} tid=${threadIdDec} state=${state} ";
035
036        private Threads()
037        {
038        }
039
040        /**
041         * Creates a dump of all the threads' state and stack traces similar to what JVM produces when
042         * signal SIGQUIT is send to the process on Unix machine.
043         * <p>
044         * Note: This is a best effort to dump as much information as possible because the Java API
045         * doesn't provide means to get all the information that is produced by jstack program for
046         * example.
047         * </p>
048         * 
049         * @param logger
050         *            the logger where the collected information will be written
051         */
052        public static void dumpAllThreads(Logger logger)
053        {
054                Args.notNull(logger, "logger");
055                if (!logger.isWarnEnabled())
056                {
057                        return;
058                }
059
060                RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
061
062                StringBuilder dump = new StringBuilder();
063
064                dump.append("Full thread dump ")
065                        .append(runtimeMXBean.getVmName())
066                        .append('(')
067                        .append(runtimeMXBean.getVmVersion())
068                        .append(')');
069                logger.warn(dump.toString());
070
071                Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
072                for (Entry<Thread, StackTraceElement[]> entry : allStackTraces.entrySet())
073                {
074                        dumpSingleThread(logger, entry.getKey(), entry.getValue());
075                }
076        }
077
078        /**
079         * Creates a dump of the threads' state and stack traces similar to the one that the JVM
080         * produces when signal SIGQUIT is send to the process on Unix machine.
081         * <p>
082         * Note: This is a best effort to dump as much information as possible because the Java API
083         * doesn't provide means to get all the information that is produced by jstack program for
084         * example.
085         * </p>
086         * 
087         * @param logger
088         *            the logger where the collected information will be written
089         * @param thread
090         *            the thread to dump
091         */
092        public static void dumpSingleThread(Logger logger, Thread thread)
093        {
094                Args.notNull(logger, "logger");
095                if (!logger.isWarnEnabled())
096                {
097                        return;
098                }
099
100                dumpSingleThread(logger, thread, thread.getStackTrace());
101        }
102
103        private static void dumpSingleThread(Logger logger, Thread thread, StackTraceElement[] trace)
104        {
105                Map<CharSequence, Object> variables = new HashMap<>();
106                variables.put("name", thread.getName());
107                variables.put("isDaemon", thread.isDaemon() ? " daemon" : "");
108                variables.put("priority", thread.getPriority());
109                variables.put("threadIdDec", thread.getId());
110                variables.put("state", thread.getState());
111
112                ThreadDump throwable = new ThreadDump();
113                throwable.setStackTrace(trace);
114                logger.warn(MapVariableInterpolator.interpolate(FORMAT, variables), throwable);
115        }
116
117        /**
118         * An exception used to hold the stacktrace of a thread.
119         */
120        private static class ThreadDump extends RuntimeException
121        {
122                private static final long serialVersionUID = 1L;
123
124                /**
125                 * @see java.lang.Throwable#fillInStackTrace()
126                 */
127                @Override
128                public synchronized Throwable fillInStackTrace()
129                {
130                        // don't waste time to load the stack of the current thread
131                        return null;
132                }
133        }
134}