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        public String getFormattedMessage() {
085            if (formattedMessage != null) {
086                return formattedMessage;
087            }
088            final StringBuilder sb = new StringBuilder(title);
089            if (title.length() > 0) {
090                sb.append("\n");
091            }
092            for (final Map.Entry<ThreadInformation, StackTraceElement[]> entry : threads.entrySet()) {
093                final ThreadInformation info = entry.getKey();
094                info.printThreadInfo(sb);
095                info.printStack(sb, entry.getValue());
096                sb.append("\n");
097            }
098            return sb.toString();
099        }
100    
101        /**
102         * Returns the title.
103         * @return the title.
104         */
105        public String getFormat() {
106            return title == null ? "" : title;
107        }
108    
109        /**
110         * Returns an array with a single element, a Map containing the ThreadInformation as the key.
111         * and the StackTraceElement array as the value;
112         * @return the "parameters" to this Message.
113         */
114        public Object[] getParameters() {
115            return null;
116        }
117    
118            /**
119         * Creates a ThreadDumpMessageProxy that can be serialized.
120         * @return a ThreadDumpMessageProxy.
121         */
122        protected Object writeReplace() {
123            return new ThreadDumpMessageProxy(this);
124        }
125    
126        private void readObject(final ObjectInputStream stream)
127            throws InvalidObjectException {
128            throw new InvalidObjectException("Proxy required");
129        }
130    
131        /**
132         * Proxy pattern used to serialize the ThreadDumpMessage.
133         */
134        private static class ThreadDumpMessageProxy implements Serializable {
135    
136            private static final long serialVersionUID = -3476620450287648269L;
137            private final String formattedMsg;
138            private final String title;
139    
140            public ThreadDumpMessageProxy(final ThreadDumpMessage msg) {
141                this.formattedMsg = msg.getFormattedMessage();
142                this.title = msg.title;
143            }
144    
145            /**
146             * Returns a ThreadDumpMessage using the data in the proxy.
147             * @return a ThreadDumpMessage.
148             */
149            protected Object readResolve() {
150                return new ThreadDumpMessage(formattedMsg, title);
151            }
152        }
153    
154        /**
155         * Factory to create Thread information.
156         */
157        private interface ThreadInfoFactory {
158            Map<ThreadInformation, StackTraceElement[]> createThreadInfo();
159        }
160    
161        /**
162         * Factory to create basic thread information.
163         */
164        private static class BasicThreadInfoFactory implements ThreadInfoFactory {
165            public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
166                final Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
167                final Map<ThreadInformation, StackTraceElement[]> threads =
168                    new HashMap<ThreadInformation, StackTraceElement[]>(map.size());
169                for (final Map.Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {
170                    threads.put(new BasicThreadInformation(entry.getKey()), entry.getValue());
171                }
172                return threads;
173            }
174        }
175    
176        /**
177         * Factory to create extended thread information.
178         */
179        private static class ExtendedThreadInfoFactory implements ThreadInfoFactory {
180            public Map<ThreadInformation, StackTraceElement[]> createThreadInfo() {
181                final ThreadMXBean bean = ManagementFactory.getThreadMXBean();
182                final ThreadInfo[] array = bean.dumpAllThreads(true, true);
183    
184                final Map<ThreadInformation, StackTraceElement[]>  threads =
185                    new HashMap<ThreadInformation, StackTraceElement[]>(array.length);
186                for (final ThreadInfo info : array) {
187                    threads.put(new ExtendedThreadInformation(info), info.getStackTrace());
188                }
189                return threads;
190            }
191        }
192    
193        /**
194         * Always returns null.
195         *
196         * @return null
197         */
198        public Throwable getThrowable() {
199            return null;
200        }
201    }