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.core.util;
018    
019    import java.io.IOException;
020    import java.io.InterruptedIOException;
021    import java.io.LineNumberReader;
022    import java.io.PrintWriter;
023    import java.io.StringReader;
024    import java.io.StringWriter;
025    import java.lang.reflect.InvocationTargetException;
026    import java.lang.reflect.Method;
027    import java.util.ArrayList;
028    import java.util.List;
029    
030    import org.apache.logging.log4j.status.StatusLogger;
031    
032    /**
033     * Helps with Throwable objects.
034     */
035    public final class Throwables {
036    
037        private static final Method ADD_SUPPRESSED;
038    
039        private static final Method GET_SUPPRESSED;
040    
041        static {
042            Method getSuppressed = null, addSuppressed = null;
043            final Method[] methods = Throwable.class.getMethods();
044            for (final Method method : methods) {
045                if (method.getName().equals("getSuppressed")) {
046                    getSuppressed = method;
047                } else if (method.getName().equals("addSuppressed")) {
048                    addSuppressed = method;
049                }
050            }
051            GET_SUPPRESSED = getSuppressed;
052            ADD_SUPPRESSED = addSuppressed;
053        }
054    
055        /**
056         * Has no effect on Java 6 and below.
057         *
058         * @param throwable a Throwable
059         * @param suppressedThrowable a suppressed Throwable
060         * @see Throwable#addSuppressed(Throwable)
061         * @deprecated If compiling on Java 7 and above use {@link Throwable#addSuppressed(Throwable)}. Marked as deprecated because Java 6 is
062         *             deprecated.
063         */
064        @Deprecated
065        public static void addSuppressed(final Throwable throwable, final Throwable suppressedThrowable) {
066            if (ADD_SUPPRESSED != null) {
067                try {
068                    ADD_SUPPRESSED.invoke(throwable, suppressedThrowable);
069                } catch (final IllegalAccessException e) {
070                    // Only happens on Java >= 7 if this class has a bug.
071                    StatusLogger.getLogger().error(e);
072                } catch (final IllegalArgumentException e) {
073                    // Only happens on Java >= 7 if this class has a bug.
074                    StatusLogger.getLogger().error(e);
075                } catch (final InvocationTargetException e) {
076                    // Only happens on Java >= 7 if this class has a bug.
077                    StatusLogger.getLogger().error(e);
078                }
079            }
080    
081        }
082    
083        /**
084         * Has no effect on Java 6 and below.
085         *
086         * @param throwable a Throwable
087         * @return see Java 7's {@link Throwable#getSuppressed()}
088         * @see Throwable#getSuppressed()
089         * @deprecated If compiling on Java 7 and above use {@link Throwable#getSuppressed()}. Marked as deprecated because Java 6 is
090         *             deprecated.
091         */
092        @Deprecated
093        public static Throwable[] getSuppressed(final Throwable throwable) {
094            if (GET_SUPPRESSED != null) {
095                try {
096                    return (Throwable[]) GET_SUPPRESSED.invoke(throwable);
097                } catch (final Exception e) {
098                    // Only happens on Java >= 7 if this class has a bug.
099                    StatusLogger.getLogger().error(e);
100                    return null;
101                }
102            }
103            return null;
104        }
105    
106        /**
107         * Returns true if the getSuppressed method is available.
108         * 
109         * @return True if getSuppressed is available.
110         */
111        public static boolean isGetSuppressedAvailable() {
112            return GET_SUPPRESSED != null;
113        }
114    
115        /**
116         * Converts a Throwable stack trace into a List of Strings
117         *
118         * @param throwable the Throwable
119         * @return a List of Strings
120         */
121        public static List<String> toStringList(final Throwable throwable) {
122            final StringWriter sw = new StringWriter();
123            final PrintWriter pw = new PrintWriter(sw);
124            try {
125                throwable.printStackTrace(pw);
126            } catch (final RuntimeException ex) {
127                // Ignore any exceptions.
128            }
129            pw.flush();
130            final List<String> lines = new ArrayList<String>();
131            final LineNumberReader reader = new LineNumberReader(new StringReader(sw.toString()));
132            try {
133                String line = reader.readLine();
134                while (line != null) {
135                    lines.add(line);
136                    line = reader.readLine();
137                }
138            } catch (final IOException ex) {
139                if (ex instanceof InterruptedIOException) {
140                    Thread.currentThread().interrupt();
141                }
142                lines.add(ex.toString());
143            } finally {
144                Closer.closeSilently(reader);
145            }
146            return lines;
147        }
148    
149        private Throwables() {
150        }
151    
152    }