1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender.db.jpa.converter;
18
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Field;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.List;
24 import java.util.ListIterator;
25 import javax.persistence.AttributeConverter;
26 import javax.persistence.Converter;
27
28
29
30
31
32 @Converter(autoApply = false)
33 public class ThrowableAttributeConverter implements AttributeConverter<Throwable, String> {
34 private static final int CAUSED_BY_STRING_LENGTH = 10;
35
36 private static final Field THROWABLE_CAUSE;
37
38 private static final Field THROWABLE_MESSAGE;
39
40 static {
41 try {
42 THROWABLE_CAUSE = Throwable.class.getDeclaredField("cause");
43 THROWABLE_CAUSE.setAccessible(true);
44 THROWABLE_MESSAGE = Throwable.class.getDeclaredField("detailMessage");
45 THROWABLE_MESSAGE.setAccessible(true);
46 } catch (NoSuchFieldException e) {
47 throw new IllegalStateException("Something is wrong with java.lang.Throwable.", e);
48 }
49 }
50
51 @Override
52 public String convertToDatabaseColumn(final Throwable throwable) {
53 StringBuilder builder = new StringBuilder();
54 this.convertThrowable(builder, throwable);
55 return builder.toString();
56 }
57
58 private void convertThrowable(final StringBuilder builder, final Throwable throwable) {
59 builder.append(throwable.toString()).append('\n');
60 for (StackTraceElement element : throwable.getStackTrace()) {
61 builder.append("\tat ").append(element).append('\n');
62 }
63 if (throwable.getCause() != null) {
64 builder.append("Caused by ");
65 this.convertThrowable(builder, throwable.getCause());
66 }
67 }
68
69 @Override
70 public Throwable convertToEntityAttribute(final String s) {
71 if (s == null || s.length() == 0) {
72 return null;
73 }
74
75 List<String> lines = Arrays.asList(s.split("(\n|\r\n)"));
76 return this.convertString(lines.listIterator(), false);
77 }
78
79 private Throwable convertString(final ListIterator<String> lines, boolean removeCausedBy) {
80 String firstLine = lines.next();
81 if (removeCausedBy) {
82 firstLine = firstLine.substring(CAUSED_BY_STRING_LENGTH);
83 }
84 int colon = firstLine.indexOf(":");
85 String throwableClassName;
86 String message = null;
87 if (colon > 1) {
88 throwableClassName = firstLine.substring(0, colon);
89 if (firstLine.length() > colon + 1) {
90 message = firstLine.substring(colon + 1).trim();
91 }
92 } else {
93 throwableClassName = firstLine;
94 }
95
96 List<StackTraceElement> stackTrace = new ArrayList<StackTraceElement>();
97 Throwable cause = null;
98 while (lines.hasNext()) {
99 String line = lines.next();
100
101 if (line.startsWith("Caused by ")) {
102 lines.previous();
103 cause = convertString(lines, true);
104 break;
105 }
106
107 stackTrace.add(
108 StackTraceElementAttributeConverter.convertString(line.trim().substring(3).trim())
109 );
110 }
111
112 return this.getThrowable(throwableClassName, message, cause,
113 stackTrace.toArray(new StackTraceElement[stackTrace.size()]));
114 }
115
116 private Throwable getThrowable(final String throwableClassName, final String message, final Throwable cause,
117 final StackTraceElement[] stackTrace) {
118 try {
119 @SuppressWarnings("unchecked")
120 Class<Throwable> throwableClass = (Class<Throwable>) Class.forName(throwableClassName);
121
122 if (!Throwable.class.isAssignableFrom(throwableClass)) {
123 return null;
124 }
125
126 Throwable throwable;
127 if (message != null && cause != null) {
128 throwable = this.getThrowable(throwableClass, message, cause);
129 if (throwable == null) {
130 throwable = this.getThrowable(throwableClass, cause);
131 if (throwable == null) {
132 throwable = this.getThrowable(throwableClass, message);
133 if (throwable == null) {
134 throwable = this.getThrowable(throwableClass);
135 if (throwable != null) {
136 THROWABLE_MESSAGE.set(throwable, message);
137 THROWABLE_CAUSE.set(throwable, cause);
138 }
139 } else {
140 THROWABLE_CAUSE.set(throwable, cause);
141 }
142 } else {
143 THROWABLE_MESSAGE.set(throwable, message);
144 }
145 }
146 } else if (cause != null) {
147 throwable = this.getThrowable(throwableClass, cause);
148 if (throwable == null) {
149 throwable = this.getThrowable(throwableClass);
150 if (throwable != null) {
151 THROWABLE_CAUSE.set(throwable, cause);
152 }
153 }
154 } else if (message != null) {
155 throwable = this.getThrowable(throwableClass, message);
156 if (throwable == null) {
157 throwable = this.getThrowable(throwableClass);
158 if (throwable != null) {
159 THROWABLE_MESSAGE.set(throwable, cause);
160 }
161 }
162 } else {
163 throwable = this.getThrowable(throwableClass);
164 }
165
166 if (throwable == null) {
167 return null;
168 }
169 throwable.setStackTrace(stackTrace);
170 return throwable;
171 } catch (Exception e) {
172 return null;
173 }
174 }
175
176 private Throwable getThrowable(final Class<Throwable> throwableClass, final String message, final Throwable cause) {
177 try {
178 @SuppressWarnings("unchecked")
179 Constructor<Throwable>[] constructors = (Constructor<Throwable>[]) throwableClass.getConstructors();
180 for (Constructor<Throwable> constructor : constructors) {
181 Class<?>[] parameterTypes = constructor.getParameterTypes();
182 if (parameterTypes.length == 2) {
183 if (String.class == parameterTypes[0] && Throwable.class.isAssignableFrom(parameterTypes[1])) {
184 return constructor.newInstance(message, cause);
185 } else if (String.class == parameterTypes[1] &&
186 Throwable.class.isAssignableFrom(parameterTypes[0])) {
187 return constructor.newInstance(cause, message);
188 }
189 }
190 }
191 return null;
192 } catch (Exception e) {
193 return null;
194 }
195 }
196
197 private Throwable getThrowable(final Class<Throwable> throwableClass, final Throwable cause) {
198 try {
199 @SuppressWarnings("unchecked")
200 Constructor<Throwable>[] constructors = (Constructor<Throwable>[]) throwableClass.getConstructors();
201 for (Constructor<Throwable> constructor : constructors) {
202 Class<?>[] parameterTypes = constructor.getParameterTypes();
203 if (parameterTypes.length == 1 && Throwable.class.isAssignableFrom(parameterTypes[0])) {
204 return constructor.newInstance(cause);
205 }
206 }
207 return null;
208 } catch (Exception e) {
209 return null;
210 }
211 }
212
213 private Throwable getThrowable(final Class<Throwable> throwableClass, final String message) {
214 try {
215 return throwableClass.getConstructor(String.class).newInstance(message);
216 } catch (Exception e) {
217 return null;
218 }
219 }
220
221 private Throwable getThrowable(final Class<Throwable> throwableClass) {
222 try {
223 return throwableClass.newInstance();
224 } catch (Exception e) {
225 return null;
226 }
227 }
228 }