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