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     */
017    package org.apache.logging.log4j.message;
018    
019    import java.io.InvalidObjectException;
020    import java.io.ObjectInputStream;
021    import java.io.Serializable;
022    import java.lang.management.ManagementFactory;
023    import java.lang.management.ThreadInfo;
024    import java.lang.management.ThreadMXBean;
025    import java.lang.reflect.Method;
026    import java.util.HashMap;
027    import java.util.Map;
028    
029    /**
030     * Captures information about all running Threads.
031     */
032    public class ThreadDumpMessage implements Message {
033    
034        private static final long serialVersionUID = -1103400781608841088L;
035    
036        private static final ThreadInfoFactory FACTORY;
037    
038        private volatile Map<ThreadInformation, StackTraceElement[]> threads;
039    
040        private final String title;
041    
042        private String formattedMessage;
043    
044        static {
045            final Method[] methods = ThreadInfo.class.getMethods();
046            boolean basic = true;
047            for (final Method method : methods) {
048                if (method.getName().equals("getLockInfo")) {
049                    basic = false;
050                    break;
051                }
052            }
053            FACTORY = basic ? new BasicThreadInfoFactory() : new ExtendedThreadInfoFactory();
054        }
055    
056        /**
057         * Generate a ThreadDumpMessage with a title.
058         * @param title The title.
059         */
060        public ThreadDumpMessage(final String title) {
061            this.title = title == null ? "" : title;
062            threads = FACTORY.createThreadInfo();
063        }
064    
065        private ThreadDumpMessage(final String formattedMsg, final String title) {
066            this.formattedMessage = formattedMsg;
067            this.title = title == null ? "" : title;
068        }
069    
070        @Override
071        public String toString() {
072            final StringBuilder sb = new StringBuilder("ThreadDumpMessage[");
073            if (this.title.length() > 0) {
074                sb.append("Title=\"").append(this.title).append("\"");
075            }
076            sb.append("]");
077            return sb.toString();
078        }
079    
080        /**
081         * Returns the ThreadDump in printable format.
082         * @return the ThreadDump suitable for logging.
083         */
084        @Override
085        public String getFormattedMessage() {
086            if (formattedMessage != null) {
087                return formattedMessage;
088            }
089            final StringBuilder sb = new StringBuilder(title);
090            if (title.length() > 0) {
091                sb.append("\n");
092            }
093            for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) {
094                final ThreadInformation info = entry.getKey();
095                info.printThreadInfo(sb);
096                info.printStack(sb, entry.getValue());
097                sb.append("\n");
098            }
099            return sb.toString();
100        }
101    
102        /**
103         * Returns the title.
104         * @return the title.
105         */
106        @Override
107        public String getFormat() {
108            return title == null ? "" : title;
109        }
110    
111        /**
112         * Returns an array with a single element, a Map containing the ThreadInformation as the key.
113         * and the StackTraceElement array as the value;
114         * @return the "parameters" to this Message.
115         */
116        @Override
117        public Object[] getParameters() {
118            return null;
119        }
120    
121            /**
122         * Creates a ThreadDumpMessageProxy that can be serialized.
123         * @return a ThreadDumpMessageProxy.
124         */
125        protected Object writeReplace() {
126            return new ThreadDumpMessageProxy(this);
127        }
128    
129        private void readObject(final ObjectInputStream stream)
130            throws InvalidObjectException {
131            throw new InvalidObjectException("Proxy required");
132        }
133    
134        /**
135         * Proxy pattern used to serialize the ThreadDumpMessage.
136         */
137        private static class ThreadDumpMessageProxy implements Serializable {
138    
139            private static final long serialVersionUID = -3476620450287648269L;
140            private final String formattedMsg;
141            private final String title;
142    
143            public ThreadDumpMessageProxy(final ThreadDumpMessage msg) {
144                this.formattedMsg = msg.getFormattedMessage();
145                this.title = msg.title;
146            }
147    
148            /**
149             * Returns a ThreadDumpMessage using the data in the proxy.
150             * @return a ThreadDumpMessage.
151             */
152            protected Object readResolve() {
153                return new ThreadDumpMessage(formattedMsg, title);
154            }
155        }
156    
157        /**
158         * Factory to create Thread information.
159         */
160        private interface ThreadInfoFactory {
161            Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
162        }
163    
164        /**
165         * Factory to create basic thread information.
166         */
167        private static class BasicThreadInfoFactory implements ThreadInfoFactory {
168            @Override
169            public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
170                final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
171                final Map<ThreadInformation, StackTraceElement[]> threads =
172                    new HashMap<ThreadInformation, StackTraceElement[]>(map.size());
173                for (final Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
174                    threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue());
175                }
176                return threads;
177            }
178        }
179    
180        /**
181         * Factory to create extended thread information.
182         */
183        private static class ExtendedThreadInfoFactory implements ThreadInfoFactory {
184            @Override
185            public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
186                final ThreadMXBean bean = ManagementFactory.getThreadMXBean();
187                final ThreadInfo[] array = bean.dumpAllThreads(true, true);
188    
189                final Map<ThreadInformation, StackTraceElement[]>  threads =
190                    new HashMap<ThreadInformation, StackTraceElement[]>(array.length);
191                for (final ThreadInfo info : array) {
192                    threads.put(new ExtendedThreadInformation(info), info.getStackTrace());
193                }
194                return threads;
195            }
196        }
197    
198        /**
199         * Always returns null.
200         *
201         * @return null
202         */
203        @Override
204        public Throwable getThrowable() {
205            return null;
206        }
207    }