1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache license, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the license for the specific language governing permissions and
15 * limitations under the license.
16 */
17 package org.apache.logging.log4j.core.util;
18
19 import java.io.IOException;
20 import java.io.InterruptedIOException;
21 import java.io.LineNumberReader;
22 import java.io.PrintWriter;
23 import java.io.StringReader;
24 import java.io.StringWriter;
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.lang.reflect.UndeclaredThrowableException;
28 import java.util.ArrayList;
29 import java.util.List;
30
31 import org.apache.logging.log4j.status.StatusLogger;
32
33 /**
34 * Helps with Throwable objects.
35 */
36 public final class Throwables {
37
38 private static final Method ADD_SUPPRESSED;
39
40 private static final Method GET_SUPPRESSED;
41
42 static {
43 Method getSuppressed = null, addSuppressed = null;
44 final Method[] methods = Throwable.class.getMethods();
45 for (final Method method : methods) {
46 if (method.getName().equals("getSuppressed")) {
47 getSuppressed = method;
48 } else if (method.getName().equals("addSuppressed")) {
49 addSuppressed = method;
50 }
51 }
52 GET_SUPPRESSED = getSuppressed;
53 ADD_SUPPRESSED = addSuppressed;
54 }
55
56 /**
57 * Has no effect on Java 6 and below.
58 *
59 * @param throwable a Throwable
60 * @param suppressedThrowable a suppressed Throwable
61 * @see Throwable#addSuppressed(Throwable)
62 * @deprecated If compiling on Java 7 and above use {@link Throwable#addSuppressed(Throwable)}. Marked as deprecated because Java 6 is
63 * deprecated.
64 */
65 @Deprecated
66 public static void addSuppressed(final Throwable throwable, final Throwable suppressedThrowable) {
67 if (ADD_SUPPRESSED != null) {
68 try {
69 ADD_SUPPRESSED.invoke(throwable, suppressedThrowable);
70 } catch (final IllegalAccessException e) {
71 // Only happens on Java >= 7 if this class has a bug.
72 StatusLogger.getLogger().error(e);
73 } catch (final IllegalArgumentException e) {
74 // Only happens on Java >= 7 if this class has a bug.
75 StatusLogger.getLogger().error(e);
76 } catch (final InvocationTargetException e) {
77 // Only happens on Java >= 7 if this class has a bug.
78 StatusLogger.getLogger().error(e);
79 }
80 }
81
82 }
83
84 /**
85 * Has no effect on Java 6 and below.
86 *
87 * @param throwable a Throwable
88 * @return see Java 7's {@link Throwable#getSuppressed()}
89 * @see Throwable#getSuppressed()
90 * @deprecated If compiling on Java 7 and above use {@link Throwable#getSuppressed()}. Marked as deprecated because Java 6 is
91 * deprecated.
92 */
93 @Deprecated
94 public static Throwable[] getSuppressed(final Throwable throwable) {
95 if (GET_SUPPRESSED != null) {
96 try {
97 return (Throwable[]) GET_SUPPRESSED.invoke(throwable);
98 } catch (final Exception e) {
99 // 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 }