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.autoconfigure.condition; 018 019import org.apache.commons.logging.Log; 020import org.apache.commons.logging.LogFactory; 021 022import org.springframework.context.annotation.Condition; 023import org.springframework.context.annotation.ConditionContext; 024import org.springframework.core.type.AnnotatedTypeMetadata; 025import org.springframework.core.type.AnnotationMetadata; 026import org.springframework.core.type.ClassMetadata; 027import org.springframework.core.type.MethodMetadata; 028import org.springframework.util.ClassUtils; 029import org.springframework.util.StringUtils; 030 031/** 032 * Base of all {@link Condition} implementations used with Spring Boot. Provides sensible 033 * logging to help the user diagnose what classes are loaded. 034 * 035 * @author Phillip Webb 036 * @author Greg Turnquist 037 */ 038public abstract class SpringBootCondition implements Condition { 039 040 private final Log logger = LogFactory.getLog(getClass()); 041 042 @Override 043 public final boolean matches(ConditionContext context, 044 AnnotatedTypeMetadata metadata) { 045 String classOrMethodName = getClassOrMethodName(metadata); 046 try { 047 ConditionOutcome outcome = getMatchOutcome(context, metadata); 048 logOutcome(classOrMethodName, outcome); 049 recordEvaluation(context, classOrMethodName, outcome); 050 return outcome.isMatch(); 051 } 052 catch (NoClassDefFoundError ex) { 053 throw new IllegalStateException( 054 "Could not evaluate condition on " + classOrMethodName + " due to " 055 + ex.getMessage() + " not " 056 + "found. Make sure your own configuration does not rely on " 057 + "that class. This can also happen if you are " 058 + "@ComponentScanning a springframework package (e.g. if you " 059 + "put a @ComponentScan in the default package by mistake)", 060 ex); 061 } 062 catch (RuntimeException ex) { 063 throw new IllegalStateException( 064 "Error processing condition on " + getName(metadata), ex); 065 } 066 } 067 068 private String getName(AnnotatedTypeMetadata metadata) { 069 if (metadata instanceof AnnotationMetadata) { 070 return ((AnnotationMetadata) metadata).getClassName(); 071 } 072 if (metadata instanceof MethodMetadata) { 073 MethodMetadata methodMetadata = (MethodMetadata) metadata; 074 return methodMetadata.getDeclaringClassName() + "." 075 + methodMetadata.getMethodName(); 076 } 077 return metadata.toString(); 078 } 079 080 private static String getClassOrMethodName(AnnotatedTypeMetadata metadata) { 081 if (metadata instanceof ClassMetadata) { 082 ClassMetadata classMetadata = (ClassMetadata) metadata; 083 return classMetadata.getClassName(); 084 } 085 MethodMetadata methodMetadata = (MethodMetadata) metadata; 086 return methodMetadata.getDeclaringClassName() + "#" 087 + methodMetadata.getMethodName(); 088 } 089 090 protected final void logOutcome(String classOrMethodName, ConditionOutcome outcome) { 091 if (this.logger.isTraceEnabled()) { 092 this.logger.trace(getLogMessage(classOrMethodName, outcome)); 093 } 094 } 095 096 private StringBuilder getLogMessage(String classOrMethodName, 097 ConditionOutcome outcome) { 098 StringBuilder message = new StringBuilder(); 099 message.append("Condition "); 100 message.append(ClassUtils.getShortName(getClass())); 101 message.append(" on "); 102 message.append(classOrMethodName); 103 message.append(outcome.isMatch() ? " matched" : " did not match"); 104 if (StringUtils.hasLength(outcome.getMessage())) { 105 message.append(" due to "); 106 message.append(outcome.getMessage()); 107 } 108 return message; 109 } 110 111 private void recordEvaluation(ConditionContext context, String classOrMethodName, 112 ConditionOutcome outcome) { 113 if (context.getBeanFactory() != null) { 114 ConditionEvaluationReport.get(context.getBeanFactory()) 115 .recordConditionEvaluation(classOrMethodName, this, outcome); 116 } 117 } 118 119 /** 120 * Determine the outcome of the match along with suitable log output. 121 * @param context the condition context 122 * @param metadata the annotation metadata 123 * @return the condition outcome 124 */ 125 public abstract ConditionOutcome getMatchOutcome(ConditionContext context, 126 AnnotatedTypeMetadata metadata); 127 128 /** 129 * Return true if any of the specified conditions match. 130 * @param context the context 131 * @param metadata the annotation meta-data 132 * @param conditions conditions to test 133 * @return {@code true} if any condition matches. 134 */ 135 protected final boolean anyMatches(ConditionContext context, 136 AnnotatedTypeMetadata metadata, Condition... conditions) { 137 for (Condition condition : conditions) { 138 if (matches(context, metadata, condition)) { 139 return true; 140 } 141 } 142 return false; 143 } 144 145 /** 146 * Return true if any of the specified condition matches. 147 * @param context the context 148 * @param metadata the annotation meta-data 149 * @param condition condition to test 150 * @return {@code true} if the condition matches. 151 */ 152 protected final boolean matches(ConditionContext context, 153 AnnotatedTypeMetadata metadata, Condition condition) { 154 if (condition instanceof SpringBootCondition) { 155 return ((SpringBootCondition) condition).getMatchOutcome(context, metadata) 156 .isMatch(); 157 } 158 return condition.matches(context, metadata); 159 } 160 161}