001/* 002 * Copyright 2012-2017 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 * http://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.boot.diagnostics.analyzer; 018 019import org.springframework.beans.BeanInstantiationException; 020import org.springframework.beans.factory.InjectionPoint; 021import org.springframework.beans.factory.UnsatisfiedDependencyException; 022import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; 023import org.springframework.boot.diagnostics.FailureAnalysis; 024import org.springframework.boot.diagnostics.FailureAnalyzer; 025import org.springframework.util.ClassUtils; 026 027/** 028 * Abstract base class for a {@link FailureAnalyzer} that handles some kind of injection 029 * failure. 030 * 031 * @param <T> the type of exception to analyze 032 * @author Andy Wilkinson 033 * @author Stephane Nicoll 034 * @since 1.4.1 035 */ 036public abstract class AbstractInjectionFailureAnalyzer<T extends Throwable> 037 extends AbstractFailureAnalyzer<T> { 038 039 @Override 040 protected final FailureAnalysis analyze(Throwable rootFailure, T cause) { 041 return analyze(rootFailure, cause, getDescription(rootFailure)); 042 } 043 044 private String getDescription(Throwable rootFailure) { 045 UnsatisfiedDependencyException unsatisfiedDependency = findMostNestedCause( 046 rootFailure, UnsatisfiedDependencyException.class); 047 if (unsatisfiedDependency != null) { 048 return getDescription(unsatisfiedDependency); 049 } 050 BeanInstantiationException beanInstantiationException = findMostNestedCause( 051 rootFailure, BeanInstantiationException.class); 052 if (beanInstantiationException != null) { 053 return getDescription(beanInstantiationException); 054 } 055 return null; 056 } 057 058 @SuppressWarnings("unchecked") 059 private <C extends Exception> C findMostNestedCause(Throwable root, Class<C> type) { 060 Throwable candidate = root; 061 C result = null; 062 while (candidate != null) { 063 if (type.isAssignableFrom(candidate.getClass())) { 064 result = (C) candidate; 065 } 066 candidate = candidate.getCause(); 067 } 068 return result; 069 } 070 071 private String getDescription(UnsatisfiedDependencyException ex) { 072 InjectionPoint injectionPoint = ex.getInjectionPoint(); 073 if (injectionPoint != null) { 074 if (injectionPoint.getField() != null) { 075 return String.format("Field %s in %s", 076 injectionPoint.getField().getName(), 077 injectionPoint.getField().getDeclaringClass().getName()); 078 } 079 if (injectionPoint.getMethodParameter() != null) { 080 if (injectionPoint.getMethodParameter().getConstructor() != null) { 081 return String.format("Parameter %d of constructor in %s", 082 injectionPoint.getMethodParameter().getParameterIndex(), 083 injectionPoint.getMethodParameter().getDeclaringClass() 084 .getName()); 085 } 086 return String.format("Parameter %d of method %s in %s", 087 injectionPoint.getMethodParameter().getParameterIndex(), 088 injectionPoint.getMethodParameter().getMethod().getName(), 089 injectionPoint.getMethodParameter().getDeclaringClass() 090 .getName()); 091 } 092 } 093 return ex.getResourceDescription(); 094 } 095 096 private String getDescription(BeanInstantiationException ex) { 097 if (ex.getConstructingMethod() != null) { 098 return String.format("Method %s in %s", ex.getConstructingMethod().getName(), 099 ex.getConstructingMethod().getDeclaringClass().getName()); 100 } 101 if (ex.getConstructor() != null) { 102 return String.format("Constructor in %s", ClassUtils 103 .getUserClass(ex.getConstructor().getDeclaringClass()).getName()); 104 } 105 return ex.getBeanClass().getName(); 106 } 107 108 /** 109 * Returns an analysis of the given {@code rootFailure}, or {@code null} if no 110 * analysis was possible. 111 * @param rootFailure the root failure passed to the analyzer 112 * @param cause the actual found cause 113 * @param description the description of the injection point or {@code null} 114 * @return the analysis or {@code null} 115 */ 116 protected abstract FailureAnalysis analyze(Throwable rootFailure, T cause, 117 String description); 118 119}