001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.core; 018 019import org.springframework.lang.Nullable; 020 021/** 022 * Handy class for wrapping runtime {@code Exceptions} with a root cause. 023 * 024 * <p>This class is {@code abstract} to force the programmer to extend 025 * the class. {@code getMessage} will include nested exception 026 * information; {@code printStackTrace} and other like methods will 027 * delegate to the wrapped exception, if any. 028 * 029 * <p>The similarity between this class and the {@link NestedCheckedException} 030 * class is unavoidable, as Java forces these two classes to have different 031 * superclasses (ah, the inflexibility of concrete inheritance!). 032 * 033 * @author Rod Johnson 034 * @author Juergen Hoeller 035 * @see #getMessage 036 * @see #printStackTrace 037 * @see NestedCheckedException 038 */ 039public abstract class NestedRuntimeException extends RuntimeException { 040 041 /** Use serialVersionUID from Spring 1.2 for interoperability. */ 042 private static final long serialVersionUID = 5439915454935047936L; 043 044 static { 045 // Eagerly load the NestedExceptionUtils class to avoid classloader deadlock 046 // issues on OSGi when calling getMessage(). Reported by Don Brown; SPR-5607. 047 NestedExceptionUtils.class.getName(); 048 } 049 050 051 /** 052 * Construct a {@code NestedRuntimeException} with the specified detail message. 053 * @param msg the detail message 054 */ 055 public NestedRuntimeException(String msg) { 056 super(msg); 057 } 058 059 /** 060 * Construct a {@code NestedRuntimeException} with the specified detail message 061 * and nested exception. 062 * @param msg the detail message 063 * @param cause the nested exception 064 */ 065 public NestedRuntimeException(@Nullable String msg, @Nullable Throwable cause) { 066 super(msg, cause); 067 } 068 069 070 /** 071 * Return the detail message, including the message from the nested exception 072 * if there is one. 073 */ 074 @Override 075 @Nullable 076 public String getMessage() { 077 return NestedExceptionUtils.buildMessage(super.getMessage(), getCause()); 078 } 079 080 081 /** 082 * Retrieve the innermost cause of this exception, if any. 083 * @return the innermost exception, or {@code null} if none 084 * @since 2.0 085 */ 086 @Nullable 087 public Throwable getRootCause() { 088 return NestedExceptionUtils.getRootCause(this); 089 } 090 091 /** 092 * Retrieve the most specific cause of this exception, that is, 093 * either the innermost cause (root cause) or this exception itself. 094 * <p>Differs from {@link #getRootCause()} in that it falls back 095 * to the present exception if there is no root cause. 096 * @return the most specific cause (never {@code null}) 097 * @since 2.0.3 098 */ 099 public Throwable getMostSpecificCause() { 100 Throwable rootCause = getRootCause(); 101 return (rootCause != null ? rootCause : this); 102 } 103 104 /** 105 * Check whether this exception contains an exception of the given type: 106 * either it is of the given class itself or it contains a nested cause 107 * of the given type. 108 * @param exType the exception type to look for 109 * @return whether there is a nested exception of the specified type 110 */ 111 public boolean contains(@Nullable Class<?> exType) { 112 if (exType == null) { 113 return false; 114 } 115 if (exType.isInstance(this)) { 116 return true; 117 } 118 Throwable cause = getCause(); 119 if (cause == this) { 120 return false; 121 } 122 if (cause instanceof NestedRuntimeException) { 123 return ((NestedRuntimeException) cause).contains(exType); 124 } 125 else { 126 while (cause != null) { 127 if (exType.isInstance(cause)) { 128 return true; 129 } 130 if (cause.getCause() == cause) { 131 break; 132 } 133 cause = cause.getCause(); 134 } 135 return false; 136 } 137 } 138 139}