    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    }