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}