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 */ 017package org.apache.logging.log4j.core.impl; 018 019import java.io.Serializable; 020import java.net.URL; 021import java.security.CodeSource; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029import java.util.Stack; 030 031import org.apache.logging.log4j.core.pattern.PlainTextRenderer; 032import org.apache.logging.log4j.core.pattern.TextRenderer; 033import org.apache.logging.log4j.core.util.Loader; 034import org.apache.logging.log4j.status.StatusLogger; 035import org.apache.logging.log4j.util.LoaderUtil; 036import org.apache.logging.log4j.util.StackLocatorUtil; 037import org.apache.logging.log4j.util.Strings; 038 039/** 040 * Wraps a Throwable to add packaging information about each stack trace element. 041 * 042 * <p> 043 * A proxy is used to represent a throwable that may not exist in a different class loader or JVM. When an application 044 * deserializes a ThrowableProxy, the throwable may not be set, but the throwable's information is preserved in other 045 * fields of the proxy like the message and stack trace. 046 * </p> 047 * 048 * <p> 049 * TODO: Move this class to org.apache.logging.log4j.core because it is used from LogEvent. 050 * </p> 051 * <p> 052 * TODO: Deserialize: Try to rebuild Throwable if the target exception is in this class loader? 053 * </p> 054 */ 055public class ThrowableProxy implements Serializable { 056 057 private static final String TAB = "\t"; 058 private static final String CAUSED_BY_LABEL = "Caused by: "; 059 private static final String SUPPRESSED_LABEL = "Suppressed: "; 060 private static final String WRAPPED_BY_LABEL = "Wrapped by: "; 061 062 /** 063 * Cached StackTracePackageElement and ClassLoader. 064 * <p> 065 * Consider this class private. 066 * </p> 067 */ 068 static class CacheEntry { 069 private final ExtendedClassInfo element; 070 private final ClassLoader loader; 071 072 public CacheEntry(final ExtendedClassInfo element, final ClassLoader loader) { 073 this.element = element; 074 this.loader = loader; 075 } 076 } 077 078 private static final ThrowableProxy[] EMPTY_THROWABLE_PROXY_ARRAY = new ThrowableProxy[0]; 079 080 private static final char EOL = '\n'; 081 082 private static final String EOL_STR = String.valueOf(EOL); 083 084 private static final long serialVersionUID = -2752771578252251910L; 085 086 private final ThrowableProxy causeProxy; 087 088 private int commonElementCount; 089 090 private final ExtendedStackTraceElement[] extendedStackTrace; 091 092 private final String localizedMessage; 093 094 private final String message; 095 096 private final String name; 097 098 private final ThrowableProxy[] suppressedProxies; 099 100 private final transient Throwable throwable; 101 102 /** 103 * For JSON and XML IO via Jackson. 104 */ 105 @SuppressWarnings("unused") 106 private ThrowableProxy() { 107 this.throwable = null; 108 this.name = null; 109 this.extendedStackTrace = null; 110 this.causeProxy = null; 111 this.message = null; 112 this.localizedMessage = null; 113 this.suppressedProxies = EMPTY_THROWABLE_PROXY_ARRAY; 114 } 115 116 /** 117 * Constructs the wrapper for the Throwable that includes packaging data. 118 * 119 * @param throwable The Throwable to wrap, must not be null. 120 */ 121 public ThrowableProxy(final Throwable throwable) { 122 this(throwable, null); 123 } 124 125 /** 126 * Constructs the wrapper for the Throwable that includes packaging data. 127 * 128 * @param throwable The Throwable to wrap, must not be null. 129 * @param visited The set of visited suppressed exceptions. 130 */ 131 private ThrowableProxy(final Throwable throwable, final Set<Throwable> visited) { 132 this.throwable = throwable; 133 this.name = throwable.getClass().getName(); 134 this.message = throwable.getMessage(); 135 this.localizedMessage = throwable.getLocalizedMessage(); 136 final Map<String, CacheEntry> map = new HashMap<>(); 137 final Stack<Class<?>> stack = StackLocatorUtil.getCurrentStackTrace(); 138 this.extendedStackTrace = this.toExtendedStackTrace(stack, map, null, throwable.getStackTrace()); 139 final Throwable throwableCause = throwable.getCause(); 140 final Set<Throwable> causeVisited = new HashSet<>(1); 141 this.causeProxy = throwableCause == null ? null : new ThrowableProxy(throwable, stack, map, throwableCause, 142 visited, causeVisited); 143 this.suppressedProxies = this.toSuppressedProxies(throwable, visited); 144 } 145 146 /** 147 * Constructs the wrapper for a Throwable that is referenced as the cause by another Throwable. 148 * 149 * @param parent The Throwable referencing this Throwable. 150 * @param stack The Class stack. 151 * @param map The cache containing the packaging data. 152 * @param cause The Throwable to wrap. 153 * @param suppressedVisited TODO 154 * @param causeVisited TODO 155 */ 156 private ThrowableProxy(final Throwable parent, final Stack<Class<?>> stack, final Map<String, CacheEntry> map, 157 final Throwable cause, final Set<Throwable> suppressedVisited, 158 final Set<Throwable> causeVisited) { 159 causeVisited.add(cause); 160 this.throwable = cause; 161 this.name = cause.getClass().getName(); 162 this.message = this.throwable.getMessage(); 163 this.localizedMessage = this.throwable.getLocalizedMessage(); 164 this.extendedStackTrace = this.toExtendedStackTrace(stack, map, parent.getStackTrace(), cause.getStackTrace()); 165 final Throwable causeCause = cause.getCause(); 166 this.causeProxy = causeCause == null || causeVisited.contains(causeCause) ? null : new ThrowableProxy(parent, 167 stack, map, causeCause, suppressedVisited, causeVisited); 168 this.suppressedProxies = this.toSuppressedProxies(cause, suppressedVisited); 169 } 170 171 @Override 172 public boolean equals(final Object obj) { 173 if (this == obj) { 174 return true; 175 } 176 if (obj == null) { 177 return false; 178 } 179 if (this.getClass() != obj.getClass()) { 180 return false; 181 } 182 final ThrowableProxy other = (ThrowableProxy) obj; 183 if (this.causeProxy == null) { 184 if (other.causeProxy != null) { 185 return false; 186 } 187 } else if (!this.causeProxy.equals(other.causeProxy)) { 188 return false; 189 } 190 if (this.commonElementCount != other.commonElementCount) { 191 return false; 192 } 193 if (this.name == null) { 194 if (other.name != null) { 195 return false; 196 } 197 } else if (!this.name.equals(other.name)) { 198 return false; 199 } 200 if (!Arrays.equals(this.extendedStackTrace, other.extendedStackTrace)) { 201 return false; 202 } 203 if (!Arrays.equals(this.suppressedProxies, other.suppressedProxies)) { 204 return false; 205 } 206 return true; 207 } 208 209 private void formatCause(final StringBuilder sb, final String prefix, final ThrowableProxy cause, 210 final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix) { 211 formatThrowableProxy(sb, prefix, CAUSED_BY_LABEL, cause, ignorePackages, textRenderer, suffix); 212 } 213 214 private void formatThrowableProxy(final StringBuilder sb, final String prefix, final String causeLabel, 215 final ThrowableProxy throwableProxy, final List<String> ignorePackages, 216 final TextRenderer textRenderer, final String suffix) { 217 if (throwableProxy == null) { 218 return; 219 } 220 textRenderer.render(prefix, sb, "Prefix"); 221 textRenderer.render(causeLabel, sb, "CauseLabel"); 222 throwableProxy.renderOn(sb, textRenderer); 223 renderSuffix(suffix, sb, textRenderer); 224 textRenderer.render(EOL_STR, sb, "Text"); 225 this.formatElements(sb, prefix, throwableProxy.commonElementCount, 226 throwableProxy.getStackTrace(), throwableProxy.extendedStackTrace, ignorePackages, textRenderer, suffix); 227 this.formatSuppressed(sb, prefix + TAB, throwableProxy.suppressedProxies, ignorePackages, textRenderer, suffix); 228 this.formatCause(sb, prefix, throwableProxy.causeProxy, ignorePackages, textRenderer, suffix); 229 } 230 231 void renderOn(final StringBuilder output, final TextRenderer textRenderer) { 232 final String msg = this.message; 233 textRenderer.render(this.name, output, "Name"); 234 if (msg != null) { 235 textRenderer.render(": ", output, "NameMessageSeparator"); 236 textRenderer.render(msg, output, "Message"); 237 } 238 } 239 240 private void formatSuppressed(final StringBuilder sb, final String prefix, final ThrowableProxy[] suppressedProxies, 241 final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix) { 242 if (suppressedProxies == null) { 243 return; 244 } 245 for (final ThrowableProxy suppressedProxy : suppressedProxies) { 246 formatThrowableProxy(sb, prefix, SUPPRESSED_LABEL, suppressedProxy, ignorePackages, textRenderer, suffix); 247 } 248 } 249 250 private void formatElements(final StringBuilder sb, final String prefix, final int commonCount, 251 final StackTraceElement[] causedTrace, final ExtendedStackTraceElement[] extStackTrace, 252 final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix) { 253 if (ignorePackages == null || ignorePackages.isEmpty()) { 254 for (final ExtendedStackTraceElement element : extStackTrace) { 255 this.formatEntry(element, sb, prefix, textRenderer, suffix); 256 } 257 } else { 258 int count = 0; 259 for (int i = 0; i < extStackTrace.length; ++i) { 260 if (!this.ignoreElement(causedTrace[i], ignorePackages)) { 261 if (count > 0) { 262 appendSuppressedCount(sb, prefix, count, textRenderer, suffix); 263 count = 0; 264 } 265 this.formatEntry(extStackTrace[i], sb, prefix, textRenderer, suffix); 266 } else { 267 ++count; 268 } 269 } 270 if (count > 0) { 271 appendSuppressedCount(sb, prefix, count, textRenderer, suffix); 272 } 273 } 274 if (commonCount != 0) { 275 textRenderer.render(prefix, sb, "Prefix"); 276 textRenderer.render("\t... ", sb, "More"); 277 textRenderer.render(Integer.toString(commonCount), sb, "More"); 278 textRenderer.render(" more", sb, "More"); 279 renderSuffix(suffix, sb, textRenderer); 280 textRenderer.render(EOL_STR, sb, "Text"); 281 } 282 } 283 284 private void renderSuffix(final String suffix, final StringBuilder sb, final TextRenderer textRenderer) { 285 if (!suffix.isEmpty()) { 286 textRenderer.render(" ", sb, "Suffix"); 287 textRenderer.render(suffix, sb, "Suffix"); 288 } 289 } 290 291 private void appendSuppressedCount(final StringBuilder sb, final String prefix, final int count, 292 final TextRenderer textRenderer, final String suffix) { 293 textRenderer.render(prefix, sb, "Prefix"); 294 if (count == 1) { 295 textRenderer.render("\t... ", sb, "Suppressed"); 296 } else { 297 textRenderer.render("\t... suppressed ", sb, "Suppressed"); 298 textRenderer.render(Integer.toString(count), sb, "Suppressed"); 299 textRenderer.render(" lines", sb, "Suppressed"); 300 } 301 renderSuffix(suffix, sb, textRenderer); 302 textRenderer.render(EOL_STR, sb, "Text"); 303 } 304 305 private void formatEntry(final ExtendedStackTraceElement extStackTraceElement, final StringBuilder sb, 306 final String prefix, final TextRenderer textRenderer, final String suffix) { 307 textRenderer.render(prefix, sb, "Prefix"); 308 textRenderer.render("\tat ", sb, "At"); 309 extStackTraceElement.renderOn(sb, textRenderer); 310 renderSuffix(suffix, sb, textRenderer); 311 textRenderer.render(EOL_STR, sb, "Text"); 312 } 313 314 /** 315 * Formats the specified Throwable. 316 * @param sb StringBuilder to contain the formatted Throwable. 317 * @param cause The Throwable to format. 318 * @param suffix 319 */ 320 public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final String suffix) { 321 this.formatWrapper(sb, cause, null, PlainTextRenderer.getInstance(), suffix); 322 } 323 324 /** 325 * Formats the specified Throwable. 326 * @param sb StringBuilder to contain the formatted Throwable. 327 * @param cause The Throwable to format. 328 * @param ignorePackages The List of packages to be suppressed from the trace. 329 * @param suffix 330 */ 331 @SuppressWarnings("ThrowableResultOfMethodCallIgnored") 332 public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages, final String suffix) { 333 this.formatWrapper(sb, cause, ignorePackages, PlainTextRenderer.getInstance(), suffix); 334 } 335 336 /** 337 * Formats the specified Throwable. 338 * @param sb StringBuilder to contain the formatted Throwable. 339 * @param cause The Throwable to format. 340 * @param ignorePackages The List of packages to be suppressed from the trace. 341 * @param textRenderer The text render 342 * @param suffix 343 */ 344 @SuppressWarnings("ThrowableResultOfMethodCallIgnored") 345 public void formatWrapper(final StringBuilder sb, final ThrowableProxy cause, final List<String> ignorePackages, 346 final TextRenderer textRenderer, final String suffix) { 347 final Throwable caused = cause.getCauseProxy() != null ? cause.getCauseProxy().getThrowable() : null; 348 if (caused != null) { 349 this.formatWrapper(sb, cause.causeProxy, ignorePackages, textRenderer, suffix); 350 sb.append(WRAPPED_BY_LABEL); 351 renderSuffix(suffix, sb, textRenderer); 352 } 353 cause.renderOn(sb, textRenderer); 354 renderSuffix(suffix, sb, textRenderer); 355 textRenderer.render(EOL_STR, sb, "Text"); 356 this.formatElements(sb, Strings.EMPTY, cause.commonElementCount, 357 cause.getThrowable().getStackTrace(), cause.extendedStackTrace, ignorePackages, textRenderer, suffix); 358 } 359 360 public ThrowableProxy getCauseProxy() { 361 return this.causeProxy; 362 } 363 364 /** 365 * Formats the Throwable that is the cause of this Throwable. 366 * 367 * @return The formatted Throwable that caused this Throwable. 368 * @param suffix 369 */ 370 public String getCauseStackTraceAsString(final String suffix) { 371 return this.getCauseStackTraceAsString(null, PlainTextRenderer.getInstance(), suffix); 372 } 373 374 /** 375 * Formats the Throwable that is the cause of this Throwable. 376 * 377 * @param packages The List of packages to be suppressed from the trace. 378 * @param suffix 379 * @return The formatted Throwable that caused this Throwable. 380 */ 381 public String getCauseStackTraceAsString(final List<String> packages, final String suffix) { 382 return getCauseStackTraceAsString(packages, PlainTextRenderer.getInstance(), suffix); 383 } 384 385 /** 386 * Formats the Throwable that is the cause of this Throwable. 387 * 388 * @param ignorePackages The List of packages to be suppressed from the trace. 389 * @param textRenderer the text renderer 390 * @param suffix 391 * @return The formatted Throwable that caused this Throwable. 392 */ 393 public String getCauseStackTraceAsString(final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix) { 394 final StringBuilder sb = new StringBuilder(); 395 if (this.causeProxy != null) { 396 this.formatWrapper(sb, this.causeProxy, ignorePackages, textRenderer, suffix); 397 sb.append(WRAPPED_BY_LABEL); 398 renderSuffix(suffix, sb, textRenderer); 399 } 400 this.renderOn(sb, textRenderer); 401 renderSuffix(suffix, sb, textRenderer); 402 textRenderer.render(EOL_STR, sb, "Text"); 403 this.formatElements(sb, Strings.EMPTY, 0, this.throwable.getStackTrace(), this.extendedStackTrace, 404 ignorePackages, textRenderer, suffix); 405 return sb.toString(); 406 } 407 408 /** 409 * Returns the number of elements that are being omitted because they are common with the parent Throwable's stack 410 * trace. 411 * 412 * @return The number of elements omitted from the stack trace. 413 */ 414 public int getCommonElementCount() { 415 return this.commonElementCount; 416 } 417 418 /** 419 * Gets the stack trace including packaging information. 420 * 421 * @return The stack trace including packaging information. 422 */ 423 public ExtendedStackTraceElement[] getExtendedStackTrace() { 424 return this.extendedStackTrace; 425 } 426 427 /** 428 * Format the stack trace including packaging information. 429 * 430 * @return The formatted stack trace including packaging information. 431 * @param suffix 432 */ 433 public String getExtendedStackTraceAsString() { 434 return this.getExtendedStackTraceAsString(null, PlainTextRenderer.getInstance(), ""); 435 } 436 437 /** 438 * Format the stack trace including packaging information. 439 * 440 * @return The formatted stack trace including packaging information. 441 * @param suffix 442 */ 443 public String getExtendedStackTraceAsString(final String suffix) { 444 return this.getExtendedStackTraceAsString(null, PlainTextRenderer.getInstance(), suffix); 445 } 446 447 /** 448 * Format the stack trace including packaging information. 449 * 450 * @param ignorePackages List of packages to be ignored in the trace. 451 * @param suffix 452 * @return The formatted stack trace including packaging information. 453 */ 454 public String getExtendedStackTraceAsString(final List<String> ignorePackages, final String suffix) { 455 return getExtendedStackTraceAsString(ignorePackages, PlainTextRenderer.getInstance(), suffix); 456 } 457 458 /** 459 * Format the stack trace including packaging information. 460 * 461 * @param ignorePackages List of packages to be ignored in the trace. 462 * @param textRenderer The message renderer 463 * @param suffix 464 * @return The formatted stack trace including packaging information. 465 */ 466 public String getExtendedStackTraceAsString(final List<String> ignorePackages, final TextRenderer textRenderer, final String suffix) { 467 final StringBuilder sb = new StringBuilder(1024); 468 textRenderer.render(name, sb, "Name"); 469 textRenderer.render(": ", sb, "NameMessageSeparator"); 470 textRenderer.render(this.message, sb, "Message"); 471 renderSuffix(suffix, sb, textRenderer); 472 textRenderer.render(EOL_STR, sb, "Text"); 473 final StackTraceElement[] causedTrace = this.throwable != null ? this.throwable.getStackTrace() : null; 474 this.formatElements(sb, Strings.EMPTY, 0, causedTrace, this.extendedStackTrace, ignorePackages, textRenderer, suffix); 475 this.formatSuppressed(sb, TAB, this.suppressedProxies, ignorePackages, textRenderer, suffix); 476 this.formatCause(sb, Strings.EMPTY, this.causeProxy, ignorePackages, textRenderer, suffix); 477 return sb.toString(); 478 } 479 480 public String getLocalizedMessage() { 481 return this.localizedMessage; 482 } 483 484 public String getMessage() { 485 return this.message; 486 } 487 488 /** 489 * Return the FQCN of the Throwable. 490 * 491 * @return The FQCN of the Throwable. 492 */ 493 public String getName() { 494 return this.name; 495 } 496 497 public StackTraceElement[] getStackTrace() { 498 return this.throwable == null ? null : this.throwable.getStackTrace(); 499 } 500 501 /** 502 * Gets proxies for suppressed exceptions. 503 * 504 * @return proxies for suppressed exceptions. 505 */ 506 public ThrowableProxy[] getSuppressedProxies() { 507 return this.suppressedProxies; 508 } 509 510 /** 511 * Format the suppressed Throwables. 512 * 513 * @return The formatted suppressed Throwables. 514 * @param suffix 515 */ 516 public String getSuppressedStackTrace(final String suffix) { 517 final ThrowableProxy[] suppressed = this.getSuppressedProxies(); 518 if (suppressed == null || suppressed.length == 0) { 519 return Strings.EMPTY; 520 } 521 final StringBuilder sb = new StringBuilder("Suppressed Stack Trace Elements:").append(EOL); 522 for (final ThrowableProxy proxy : suppressed) { 523 sb.append(proxy.getExtendedStackTraceAsString(suffix)); 524 } 525 return sb.toString(); 526 } 527 528 /** 529 * The throwable or null if this object is deserialized from XML or JSON. 530 * 531 * @return The throwable or null if this object is deserialized from XML or JSON. 532 */ 533 public Throwable getThrowable() { 534 return this.throwable; 535 } 536 537 @Override 538 public int hashCode() { 539 final int prime = 31; 540 int result = 1; 541 result = prime * result + (this.causeProxy == null ? 0 : this.causeProxy.hashCode()); 542 result = prime * result + this.commonElementCount; 543 result = prime * result + (this.extendedStackTrace == null ? 0 : Arrays.hashCode(this.extendedStackTrace)); 544 result = prime * result + (this.suppressedProxies == null ? 0 : Arrays.hashCode(this.suppressedProxies)); 545 result = prime * result + (this.name == null ? 0 : this.name.hashCode()); 546 return result; 547 } 548 549 private boolean ignoreElement(final StackTraceElement element, final List<String> ignorePackages) { 550 if (ignorePackages != null) { 551 final String className = element.getClassName(); 552 for (final String pkg : ignorePackages) { 553 if (className.startsWith(pkg)) { 554 return true; 555 } 556 } 557 } 558 return false; 559 } 560 561 /** 562 * Loads classes not located via Reflection.getCallerClass. 563 * 564 * @param lastLoader The ClassLoader that loaded the Class that called this Class. 565 * @param className The name of the Class. 566 * @return The Class object for the Class or null if it could not be located. 567 */ 568 private Class<?> loadClass(final ClassLoader lastLoader, final String className) { 569 // XXX: this is overly complicated 570 Class<?> clazz; 571 if (lastLoader != null) { 572 try { 573 clazz = lastLoader.loadClass(className); 574 if (clazz != null) { 575 return clazz; 576 } 577 } catch (final Throwable ignore) { 578 // Ignore exception. 579 } 580 } 581 try { 582 clazz = LoaderUtil.loadClass(className); 583 } catch (final ClassNotFoundException | NoClassDefFoundError e) { 584 return loadClass(className); 585 } catch (final SecurityException e) { 586 return null; 587 } 588 return clazz; 589 } 590 591 private Class<?> loadClass(final String className) { 592 try { 593 return Loader.loadClass(className, this.getClass().getClassLoader()); 594 } catch (final ClassNotFoundException | NoClassDefFoundError | SecurityException e) { 595 return null; 596 } 597 } 598 599 /** 600 * Construct the CacheEntry from the Class's information. 601 * 602 * @param stackTraceElement The stack trace element 603 * @param callerClass The Class. 604 * @param exact True if the class was obtained via Reflection.getCallerClass. 605 * @return The CacheEntry. 606 */ 607 private CacheEntry toCacheEntry(final StackTraceElement stackTraceElement, final Class<?> callerClass, 608 final boolean exact) { 609 String location = "?"; 610 String version = "?"; 611 ClassLoader lastLoader = null; 612 if (callerClass != null) { 613 try { 614 final CodeSource source = callerClass.getProtectionDomain().getCodeSource(); 615 if (source != null) { 616 final URL locationURL = source.getLocation(); 617 if (locationURL != null) { 618 final String str = locationURL.toString().replace('\\', '/'); 619 int index = str.lastIndexOf("/"); 620 if (index >= 0 && index == str.length() - 1) { 621 index = str.lastIndexOf("/", index - 1); 622 location = str.substring(index + 1); 623 } else { 624 location = str.substring(index + 1); 625 } 626 } 627 } 628 } catch (final Exception ex) { 629 // Ignore the exception. 630 } 631 final Package pkg = callerClass.getPackage(); 632 if (pkg != null) { 633 final String ver = pkg.getImplementationVersion(); 634 if (ver != null) { 635 version = ver; 636 } 637 } 638 try { 639 lastLoader = callerClass.getClassLoader(); 640 } catch (final SecurityException e) { 641 lastLoader = null; 642 } 643 } 644 return new CacheEntry(new ExtendedClassInfo(exact, location, version), lastLoader); 645 } 646 647 /** 648 * Resolve all the stack entries in this stack trace that are not common with the parent. 649 * 650 * @param stack The callers Class stack. 651 * @param map The cache of CacheEntry objects. 652 * @param rootTrace The first stack trace resolve or null. 653 * @param stackTrace The stack trace being resolved. 654 * @return The StackTracePackageElement array. 655 */ 656 ExtendedStackTraceElement[] toExtendedStackTrace(final Stack<Class<?>> stack, final Map<String, CacheEntry> map, 657 final StackTraceElement[] rootTrace, 658 final StackTraceElement[] stackTrace) { 659 int stackLength; 660 if (rootTrace != null) { 661 int rootIndex = rootTrace.length - 1; 662 int stackIndex = stackTrace.length - 1; 663 while (rootIndex >= 0 && stackIndex >= 0 && rootTrace[rootIndex].equals(stackTrace[stackIndex])) { 664 --rootIndex; 665 --stackIndex; 666 } 667 this.commonElementCount = stackTrace.length - 1 - stackIndex; 668 stackLength = stackIndex + 1; 669 } else { 670 this.commonElementCount = 0; 671 stackLength = stackTrace.length; 672 } 673 final ExtendedStackTraceElement[] extStackTrace = new ExtendedStackTraceElement[stackLength]; 674 Class<?> clazz = stack.isEmpty() ? null : stack.peek(); 675 ClassLoader lastLoader = null; 676 for (int i = stackLength - 1; i >= 0; --i) { 677 final StackTraceElement stackTraceElement = stackTrace[i]; 678 final String className = stackTraceElement.getClassName(); 679 // The stack returned from getCurrentStack may be missing entries for java.lang.reflect.Method.invoke() 680 // and its implementation. The Throwable might also contain stack entries that are no longer 681 // present as those methods have returned. 682 ExtendedClassInfo extClassInfo; 683 if (clazz != null && className.equals(clazz.getName())) { 684 final CacheEntry entry = this.toCacheEntry(stackTraceElement, clazz, true); 685 extClassInfo = entry.element; 686 lastLoader = entry.loader; 687 stack.pop(); 688 clazz = stack.isEmpty() ? null : stack.peek(); 689 } else { 690 final CacheEntry cacheEntry = map.get(className); 691 if (cacheEntry != null) { 692 final CacheEntry entry = cacheEntry; 693 extClassInfo = entry.element; 694 if (entry.loader != null) { 695 lastLoader = entry.loader; 696 } 697 } else { 698 final CacheEntry entry = this.toCacheEntry(stackTraceElement, 699 this.loadClass(lastLoader, className), false); 700 extClassInfo = entry.element; 701 map.put(stackTraceElement.toString(), entry); 702 if (entry.loader != null) { 703 lastLoader = entry.loader; 704 } 705 } 706 } 707 extStackTrace[i] = new ExtendedStackTraceElement(stackTraceElement, extClassInfo); 708 } 709 return extStackTrace; 710 } 711 712 @Override 713 public String toString() { 714 final String msg = this.message; 715 return msg != null ? this.name + ": " + msg : this.name; 716 } 717 718 private ThrowableProxy[] toSuppressedProxies(final Throwable thrown, Set<Throwable> suppressedVisited) { 719 try { 720 final Throwable[] suppressed = thrown.getSuppressed(); 721 if (suppressed == null) { 722 return EMPTY_THROWABLE_PROXY_ARRAY; 723 } 724 final List<ThrowableProxy> proxies = new ArrayList<>(suppressed.length); 725 if (suppressedVisited == null) { 726 suppressedVisited = new HashSet<>(proxies.size()); 727 } 728 for (int i = 0; i < suppressed.length; i++) { 729 final Throwable candidate = suppressed[i]; 730 if (!suppressedVisited.contains(candidate)) { 731 suppressedVisited.add(candidate); 732 proxies.add(new ThrowableProxy(candidate, suppressedVisited)); 733 } 734 } 735 return proxies.toArray(new ThrowableProxy[proxies.size()]); 736 } catch (final Exception e) { 737 StatusLogger.getLogger().error(e); 738 } 739 return null; 740 } 741}