View Javadoc
1   /*
2    * Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this program; if not, write to the Free Software
16   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17   *
18   * Additionally licensed with:
19   *
20   * Licensed under the Apache License, Version 2.0 (the "License");
21   * you may not use this file except in compliance with the License.
22   * You may obtain a copy of the License at
23   *
24   *      http://www.apache.org/licenses/LICENSE-2.0
25   *
26   * Unless required by applicable law or agreed to in writing, software
27   * distributed under the License is distributed on an "AS IS" BASIS,
28   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29   * See the License for the specific language governing permissions and
30   * limitations under the License.
31   */
32  package org.spf4j.base.avro;
33  
34  import java.io.IOException;
35  import java.util.List;
36  import java.util.Objects;
37  import java.util.Set;
38  import javax.annotation.Nullable;
39  import org.spf4j.base.Throwables;
40  import static org.spf4j.base.Throwables.CAUSE_CAPTION;
41  import static org.spf4j.base.Throwables.SUPPRESSED_CAPTION;
42  import static org.spf4j.base.Throwables.writeAbreviatedClassName;
43  import org.spf4j.ds.IdentityHashSet;
44  
45  /**
46   *
47   * @author Zoltan Farkas
48   */
49  public final class AThrowables {
50  
51    private AThrowables() { }
52  
53      public static void writeTo(final RemoteException t, final Appendable to, final Throwables.PackageDetail detail,
54            final boolean abbreviatedTraceElement, final String prefix) throws IOException {
55        to.append(prefix);
56        writeMessageString(to, t);
57        to.append('\n');
58        writeThrowableDetails(t.getRemoteCause(), to, detail, abbreviatedTraceElement, prefix);
59      }
60  
61     public static void writeMessageString(final Appendable to, final RemoteException t) throws IOException {
62      to.append(t.getClass().getName());
63      to.append(':');
64      to.append(t.getRemoteClass());
65      to.append('@');
66      to.append(t.getSource());
67      String message = t.getRemoteCause().getMessage();
68      if (message != null) {
69        to.append(':').append(message);
70      }
71    }
72  
73     public static void writeTo(final Throwable t, final Appendable to, final Throwables.PackageDetail detail,
74            final boolean abbreviatedTraceElement, final String prefix) throws IOException {
75      to.append(prefix);
76      writeMessageString(to, t);
77      to.append('\n');
78      writeThrowableDetails(t, to, detail, abbreviatedTraceElement, prefix);
79    }
80  
81    public static void writeThrowableDetails(final Throwable t, final Appendable to,
82            final Throwables.PackageDetail detail, final boolean abbreviatedTraceElement, final String prefix)
83            throws IOException {
84      List<StackTraceElement> trace = t.getStackTrace();
85      writeTo(trace, to, detail, abbreviatedTraceElement, prefix);
86  
87      Throwable ourCause = t.getCause();
88      List<Throwable> suppressed = t.getSuppressed();
89      if (ourCause == null && suppressed.isEmpty()) {
90        return;
91      }
92      Set<Throwable> dejaVu = new IdentityHashSet<Throwable>();
93      dejaVu.add(t);
94      // Print suppressed exceptions, if any
95      for (Throwable se : suppressed) {
96        printEnclosedStackTrace(se, to, trace, SUPPRESSED_CAPTION, prefix + "\t",
97                dejaVu, detail, abbreviatedTraceElement);
98      }
99      // Print cause, if any
100     if (ourCause != null) {
101       printEnclosedStackTrace(ourCause, to, trace, CAUSE_CAPTION, prefix, dejaVu, detail, abbreviatedTraceElement);
102     }
103   }
104 
105   public static void writeTo(final List<StackTraceElement> trace, final Appendable to,
106           final Throwables.PackageDetail detail,
107           final boolean abbreviatedTraceElement, final String prefix)
108           throws IOException {
109     StackTraceElement prevElem = null;
110     for (StackTraceElement traceElement : trace) {
111       to.append(prefix);
112       to.append("\tat ");
113       writeTo(traceElement, prevElem, to, detail, abbreviatedTraceElement);
114       to.append('\n');
115       prevElem = traceElement;
116     }
117   }
118 
119 
120   public static void writeTo(final StackTraceElement element, @Nullable final StackTraceElement previous,
121           final Appendable to, final Throwables.PackageDetail detail,
122           final boolean abbreviatedTraceElement)
123           throws IOException {
124     Method method = element.getMethod();
125     String currClassName = method.getDeclaringClass();
126     String prevClassName = previous == null ? null : previous.getMethod().getDeclaringClass();
127     if (abbreviatedTraceElement) {
128       if (currClassName.equals(prevClassName)) {
129         to.append('^');
130       } else {
131         writeAbreviatedClassName(currClassName, to);
132       }
133     } else {
134       to.append(currClassName);
135     }
136     to.append('.');
137     to.append(method.getName());
138     FileLocation location = element.getLocation();
139     FileLocation prevLocation = previous == null ? null : previous.getLocation();
140     String currFileName = location != null ? location.getFileName() : "";
141     String prevFileName = prevLocation != null ? prevLocation.getFileName() : "";
142     String fileName = currFileName;
143     if (abbreviatedTraceElement && java.util.Objects.equals(currFileName, prevFileName)) {
144       fileName = "^";
145     }
146     final int lineNumber = location != null ? location.getLineNumber() : -1;
147     if (fileName.isEmpty()) {
148       to.append("(Unknown Source)");
149     } else if (lineNumber >= 0) {
150       to.append('(').append(fileName).append(':')
151               .append(Integer.toString(lineNumber)).append(')');
152     } else {
153       to.append('(').append(fileName).append(')');
154     }
155     if (detail == Throwables.PackageDetail.NONE) {
156       return;
157     }
158     if (abbreviatedTraceElement && currClassName.equals(prevClassName)) {
159       to.append("[^]");
160       return;
161     }
162     PackageInfo presPackageInfo = previous == null ? null : previous.getPackageInfo();
163     org.spf4j.base.avro.PackageInfo pInfo = element.getPackageInfo();
164     if (abbreviatedTraceElement  && Objects.equals(pInfo, presPackageInfo)) {
165       to.append("[^]");
166       return;
167     }
168     if (!pInfo.getUrl().isEmpty() || !pInfo.getVersion().isEmpty()) {
169       String jarSourceUrl = pInfo.getUrl();
170       String version = pInfo.getVersion();
171       to.append('[');
172       if (!jarSourceUrl.isEmpty()) {
173         if (detail == Throwables.PackageDetail.SHORT) {
174           String url = jarSourceUrl;
175           int lastIndexOf = url.lastIndexOf('/');
176           if (lastIndexOf >= 0) {
177             int lpos = url.length() - 1;
178             if (lastIndexOf == lpos) {
179               int prevSlPos = url.lastIndexOf('/', lpos - 1);
180               if (prevSlPos < 0) {
181                 to.append(url);
182               } else {
183                 to.append(url, prevSlPos + 1, url.length());
184               }
185             } else {
186               to.append(url, lastIndexOf + 1, url.length());
187             }
188           } else {
189             to.append(url);
190           }
191         } else {
192           to.append(jarSourceUrl);
193         }
194       } else {
195         to.append("na");
196       }
197       if (!version.isEmpty()) {
198         to.append(':');
199         to.append(version);
200       }
201       to.append(']');
202     }
203   }
204 
205   private static void printEnclosedStackTrace(final Throwable t, final Appendable s,
206           final List<StackTraceElement> enclosingTrace,
207           final String caption,
208           final String prefix,
209           final Set<Throwable> dejaVu,
210           final Throwables.PackageDetail detail,
211           final boolean abbreviatedTraceElement) throws IOException {
212     if (dejaVu.contains(t)) {
213       s.append("\t[CIRCULAR REFERENCE:");
214       writeMessageString(s, t);
215       s.append(']');
216     } else {
217       dejaVu.add(t);
218       // Compute number of frames in common between this and enclosing trace
219       List<StackTraceElement> trace = t.getStackTrace();
220       int framesInCommon = commonFrames(trace, enclosingTrace);
221       int m = trace.size() - framesInCommon;
222       // Print our stack trace
223       s.append(prefix).append(caption);
224       writeMessageString(s, t);
225       s.append('\n');
226       StackTraceElement prev = null;
227       for (int i = 0; i < m; i++) {
228         s.append(prefix).append("\tat ");
229         StackTraceElement ste = trace.get(i);
230         writeTo(ste, prev, s, detail, abbreviatedTraceElement);
231         s.append('\n');
232         prev = ste;
233       }
234       if (framesInCommon != 0) {
235         s.append(prefix).append("\t... ").append(Integer.toString(framesInCommon)).append(" more");
236         s.append('\n');
237       }
238 
239       // Print suppressed exceptions, if any
240       for (Throwable se : t.getSuppressed()) {
241         printEnclosedStackTrace(se, s, trace, SUPPRESSED_CAPTION, prefix + '\t',
242                 dejaVu, detail, abbreviatedTraceElement);
243       }
244 
245       // Print cause, if any
246       Throwable ourCause = t.getCause();
247       if (ourCause != null) {
248         printEnclosedStackTrace(ourCause, s, trace, CAUSE_CAPTION, prefix,
249                 dejaVu, detail, abbreviatedTraceElement);
250       }
251     }
252   }
253 
254   public static int commonFrames(final List<StackTraceElement> trace,
255           final List<StackTraceElement> enclosingTrace) {
256     int from = trace.size() - 1;
257     int m = from;
258     int n = enclosingTrace.size() - 1;
259     while (m >= 0 && n >= 0 && trace.get(m).equals(enclosingTrace.get(n))) {
260       m--;
261       n--;
262     }
263     return from - m;
264   }
265 
266   public static void writeMessageString(final Appendable to, final Throwable t) throws IOException {
267     to.append(t.getClass().getName());
268     String message = t.getMessage();
269     if (message != null) {
270       to.append(':').append(message);
271     }
272   }
273 
274 }