AThrowables.java
/*
* Copyright (c) 2001-2017, Zoltan Farkas All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Additionally licensed with:
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.spf4j.base.avro;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nullable;
import org.spf4j.base.Throwables;
import static org.spf4j.base.Throwables.CAUSE_CAPTION;
import static org.spf4j.base.Throwables.SUPPRESSED_CAPTION;
import static org.spf4j.base.Throwables.writeAbreviatedClassName;
import org.spf4j.ds.IdentityHashSet;
/**
*
* @author Zoltan Farkas
*/
public final class AThrowables {
private AThrowables() { }
public static void writeTo(final RemoteException t, final Appendable to, final Throwables.PackageDetail detail,
final boolean abbreviatedTraceElement, final String prefix) throws IOException {
to.append(prefix);
writeMessageString(to, t);
to.append('\n');
writeThrowableDetails(t.getRemoteCause(), to, detail, abbreviatedTraceElement, prefix);
}
public static void writeMessageString(final Appendable to, final RemoteException t) throws IOException {
to.append(t.getClass().getName());
to.append(':');
to.append(t.getRemoteClass());
to.append('@');
to.append(t.getSource());
String message = t.getRemoteCause().getMessage();
if (message != null) {
to.append(':').append(message);
}
}
public static void writeTo(final Throwable t, final Appendable to, final Throwables.PackageDetail detail,
final boolean abbreviatedTraceElement, final String prefix) throws IOException {
to.append(prefix);
writeMessageString(to, t);
to.append('\n');
writeThrowableDetails(t, to, detail, abbreviatedTraceElement, prefix);
}
public static void writeThrowableDetails(final Throwable t, final Appendable to,
final Throwables.PackageDetail detail, final boolean abbreviatedTraceElement, final String prefix)
throws IOException {
List<StackTraceElement> trace = t.getStackTrace();
writeTo(trace, to, detail, abbreviatedTraceElement, prefix);
Throwable ourCause = t.getCause();
List<Throwable> suppressed = t.getSuppressed();
if (ourCause == null && suppressed.isEmpty()) {
return;
}
Set<Throwable> dejaVu = new IdentityHashSet<Throwable>();
dejaVu.add(t);
// Print suppressed exceptions, if any
for (Throwable se : suppressed) {
printEnclosedStackTrace(se, to, trace, SUPPRESSED_CAPTION, prefix + "\t",
dejaVu, detail, abbreviatedTraceElement);
}
// Print cause, if any
if (ourCause != null) {
printEnclosedStackTrace(ourCause, to, trace, CAUSE_CAPTION, prefix, dejaVu, detail, abbreviatedTraceElement);
}
}
public static void writeTo(final List<StackTraceElement> trace, final Appendable to,
final Throwables.PackageDetail detail,
final boolean abbreviatedTraceElement, final String prefix)
throws IOException {
StackTraceElement prevElem = null;
for (StackTraceElement traceElement : trace) {
to.append(prefix);
to.append("\tat ");
writeTo(traceElement, prevElem, to, detail, abbreviatedTraceElement);
to.append('\n');
prevElem = traceElement;
}
}
public static void writeTo(final StackTraceElement element, @Nullable final StackTraceElement previous,
final Appendable to, final Throwables.PackageDetail detail,
final boolean abbreviatedTraceElement)
throws IOException {
Method method = element.getMethod();
String currClassName = method.getDeclaringClass();
String prevClassName = previous == null ? null : previous.getMethod().getDeclaringClass();
if (abbreviatedTraceElement) {
if (currClassName.equals(prevClassName)) {
to.append('^');
} else {
writeAbreviatedClassName(currClassName, to);
}
} else {
to.append(currClassName);
}
to.append('.');
to.append(method.getName());
FileLocation location = element.getLocation();
FileLocation prevLocation = previous == null ? null : previous.getLocation();
String currFileName = location != null ? location.getFileName() : "";
String prevFileName = prevLocation != null ? prevLocation.getFileName() : "";
String fileName = currFileName;
if (abbreviatedTraceElement && java.util.Objects.equals(currFileName, prevFileName)) {
fileName = "^";
}
final int lineNumber = location != null ? location.getLineNumber() : -1;
if (fileName.isEmpty()) {
to.append("(Unknown Source)");
} else if (lineNumber >= 0) {
to.append('(').append(fileName).append(':')
.append(Integer.toString(lineNumber)).append(')');
} else {
to.append('(').append(fileName).append(')');
}
if (detail == Throwables.PackageDetail.NONE) {
return;
}
if (abbreviatedTraceElement && currClassName.equals(prevClassName)) {
to.append("[^]");
return;
}
PackageInfo presPackageInfo = previous == null ? null : previous.getPackageInfo();
org.spf4j.base.avro.PackageInfo pInfo = element.getPackageInfo();
if (abbreviatedTraceElement && Objects.equals(pInfo, presPackageInfo)) {
to.append("[^]");
return;
}
if (!pInfo.getUrl().isEmpty() || !pInfo.getVersion().isEmpty()) {
String jarSourceUrl = pInfo.getUrl();
String version = pInfo.getVersion();
to.append('[');
if (!jarSourceUrl.isEmpty()) {
if (detail == Throwables.PackageDetail.SHORT) {
String url = jarSourceUrl;
int lastIndexOf = url.lastIndexOf('/');
if (lastIndexOf >= 0) {
int lpos = url.length() - 1;
if (lastIndexOf == lpos) {
int prevSlPos = url.lastIndexOf('/', lpos - 1);
if (prevSlPos < 0) {
to.append(url);
} else {
to.append(url, prevSlPos + 1, url.length());
}
} else {
to.append(url, lastIndexOf + 1, url.length());
}
} else {
to.append(url);
}
} else {
to.append(jarSourceUrl);
}
} else {
to.append("na");
}
if (!version.isEmpty()) {
to.append(':');
to.append(version);
}
to.append(']');
}
}
private static void printEnclosedStackTrace(final Throwable t, final Appendable s,
final List<StackTraceElement> enclosingTrace,
final String caption,
final String prefix,
final Set<Throwable> dejaVu,
final Throwables.PackageDetail detail,
final boolean abbreviatedTraceElement) throws IOException {
if (dejaVu.contains(t)) {
s.append("\t[CIRCULAR REFERENCE:");
writeMessageString(s, t);
s.append(']');
} else {
dejaVu.add(t);
// Compute number of frames in common between this and enclosing trace
List<StackTraceElement> trace = t.getStackTrace();
int framesInCommon = commonFrames(trace, enclosingTrace);
int m = trace.size() - framesInCommon;
// Print our stack trace
s.append(prefix).append(caption);
writeMessageString(s, t);
s.append('\n');
StackTraceElement prev = null;
for (int i = 0; i < m; i++) {
s.append(prefix).append("\tat ");
StackTraceElement ste = trace.get(i);
writeTo(ste, prev, s, detail, abbreviatedTraceElement);
s.append('\n');
prev = ste;
}
if (framesInCommon != 0) {
s.append(prefix).append("\t... ").append(Integer.toString(framesInCommon)).append(" more");
s.append('\n');
}
// Print suppressed exceptions, if any
for (Throwable se : t.getSuppressed()) {
printEnclosedStackTrace(se, s, trace, SUPPRESSED_CAPTION, prefix + '\t',
dejaVu, detail, abbreviatedTraceElement);
}
// Print cause, if any
Throwable ourCause = t.getCause();
if (ourCause != null) {
printEnclosedStackTrace(ourCause, s, trace, CAUSE_CAPTION, prefix,
dejaVu, detail, abbreviatedTraceElement);
}
}
}
public static int commonFrames(final List<StackTraceElement> trace,
final List<StackTraceElement> enclosingTrace) {
int from = trace.size() - 1;
int m = from;
int n = enclosingTrace.size() - 1;
while (m >= 0 && n >= 0 && trace.get(m).equals(enclosingTrace.get(n))) {
m--;
n--;
}
return from - m;
}
public static void writeMessageString(final Appendable to, final Throwable t) throws IOException {
to.append(t.getClass().getName());
String message = t.getMessage();
if (message != null) {
to.append(':').append(message);
}
}
}