Commit 4895ab4b authored by Phillip Webb's avatar Phillip Webb

Create common SpringBootCondition base class

Create a new SpringBootCondition that will log why a condition has
or has not been applied. The removes the need for ConditionLogUtils
and simplifies many of the existing condition implementations.

Issue: #55203236
parent 169373c0
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.condition;
import org.apache.commons.logging.Log;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
/**
* General utilities for constructing {@code @Conditional} log messages.
*
* @author Dave Syer
*/
public abstract class ConditionLogUtils {
public static String getPrefix(Log logger, AnnotatedTypeMetadata metadata) {
String prefix = "";
if (logger.isDebugEnabled()) {
prefix = metadata instanceof ClassMetadata ? "Processing "
+ ((ClassMetadata) metadata).getClassName() + ". "
: (metadata instanceof MethodMetadata ? "Processing "
+ getMethodName((MethodMetadata) metadata) + ". " : "");
}
return prefix;
}
private static String getMethodName(MethodMetadata metadata) {
return metadata.getDeclaringClassName() + "#" + metadata.getMethodName();
}
}
......@@ -21,8 +21,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
......@@ -45,32 +43,25 @@ import org.springframework.util.ReflectionUtils.MethodCallback;
* @author Phillip Webb
* @author Dave Syer
*/
class OnBeanCondition implements ConfigurationCondition {
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {
private static final String[] NO_BEANS = {};
private final Log logger = LogFactory.getLog(getClass());
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String checking = ConditionLogUtils.getPrefix(this.logger, metadata);
public Outcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
ConditionalOnBean.class);
List<String> matching = getMatchingBeans(context, spec);
if (matching.isEmpty()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(checking + " @ConditionalOnBean " + spec
+ " found no beans (search terminated with matches=false)");
}
return false;
return Outcome.noMatch("@ConditionalOnBean " + spec + " found no beans");
}
}
......@@ -79,16 +70,12 @@ class OnBeanCondition implements ConfigurationCondition {
ConditionalOnMissingBean.class);
List<String> matching = getMatchingBeans(context, spec);
if (!matching.isEmpty()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(checking + " @ConditionalOnMissingBean " + spec
+ " found the following " + matching
+ " (search terminated with matches=false)");
}
return false;
return Outcome.noMatch("@ConditionalOnMissingBean " + spec
+ " found the following " + matching);
}
}
return true;
return Outcome.match();
}
private List<String> getMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
......
......@@ -20,8 +20,6 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
......@@ -36,14 +34,11 @@ import org.springframework.util.StringUtils;
* @see ConditionalOnClass
* @see ConditionalOnMissingClass
*/
class OnClassCondition implements Condition {
private static Log logger = LogFactory.getLog(OnClassCondition.class);
class OnClassCondition extends SpringBootCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String checking = ConditionLogUtils.getPrefix(logger, metadata);
public Outcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> onClasses = getAttributes(metadata,
ConditionalOnClass.class);
......@@ -51,13 +46,8 @@ class OnClassCondition implements Condition {
List<String> missing = getMatchingClasses(onClasses, MatchType.MISSING,
context);
if (!missing.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug(checking
+ "Required @ConditionalOnClass classes not found: "
+ StringUtils.collectionToCommaDelimitedString(missing)
+ " (search terminated with matches=false)");
}
return false;
return Outcome.noMatch("required @ConditionalOnClass classes not found: "
+ StringUtils.collectionToCommaDelimitedString(missing));
}
}
......@@ -67,20 +57,12 @@ class OnClassCondition implements Condition {
List<String> present = getMatchingClasses(onMissingClasses,
MatchType.PRESENT, context);
if (!present.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug(checking
+ "Required @ConditionalOnMissing classes found: "
+ StringUtils.collectionToCommaDelimitedString(present)
+ " (search terminated with matches=false)");
}
return false;
return Outcome.noMatch("required @ConditionalOnMissing classes found: "
+ StringUtils.collectionToCommaDelimitedString(present));
}
}
if (logger.isDebugEnabled()) {
logger.debug(checking + "Match result is: true");
}
return true;
return Outcome.match();
}
private MultiValueMap<String, Object> getAttributes(AnnotatedTypeMetadata metadata,
......
......@@ -16,12 +16,9 @@
package org.springframework.boot.autoconfigure.condition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.core.type.AnnotatedTypeMetadata;
......@@ -33,32 +30,21 @@ import org.springframework.core.type.ClassMetadata;
* @author Dave Syer
* @see ConditionalOnExpression
*/
public class OnExpressionCondition implements Condition {
private static Log logger = LogFactory.getLog(OnExpressionCondition.class);
public class OnExpressionCondition extends SpringBootCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String checking = ConditionLogUtils.getPrefix(logger, metadata);
public Outcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String value = (String) metadata.getAnnotationAttributes(
String expression = (String) metadata.getAnnotationAttributes(
ConditionalOnExpression.class.getName()).get("value");
if (!value.startsWith("#{")) {
if (!expression.startsWith("#{")) {
// For convenience allow user to provide bare expression with no #{} wrapper
value = "#{" + value + "}";
}
if (logger.isDebugEnabled()) {
StringBuilder builder = new StringBuilder(checking)
.append("Evaluating expression");
if (metadata instanceof ClassMetadata) {
builder.append(" on " + ((ClassMetadata) metadata).getClassName());
}
builder.append(": " + value);
logger.debug(builder.toString());
expression = "#{" + expression + "}";
}
// Explicitly allow environment placeholders inside the expression
value = context.getEnvironment().resolvePlaceholders(value);
expression = context.getEnvironment().resolvePlaceholders(expression);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
BeanExpressionResolver resolver = beanFactory.getBeanExpressionResolver();
BeanExpressionContext expressionContext = (beanFactory != null) ? new BeanExpressionContext(
......@@ -66,11 +52,14 @@ public class OnExpressionCondition implements Condition {
if (resolver == null) {
resolver = new StandardBeanExpressionResolver();
}
boolean result = (Boolean) resolver.evaluate(value, expressionContext);
if (logger.isDebugEnabled()) {
logger.debug(checking + "Finished matching and result is matches=" + result);
boolean result = (Boolean) resolver.evaluate(expression, expressionContext);
StringBuilder message = new StringBuilder("SpEL expression");
if (metadata instanceof ClassMetadata) {
message.append(" on " + ((ClassMetadata) metadata).getClassName());
}
return result;
message.append(": " + expression);
return new Outcome(result, message.toString());
}
}
......@@ -19,8 +19,6 @@ package org.springframework.boot.autoconfigure.condition;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.io.DefaultResourceLoader;
......@@ -35,43 +33,29 @@ import org.springframework.util.MultiValueMap;
* @author Dave Syer
* @see ConditionalOnResource
*/
class OnResourceCondition implements Condition {
private static Log logger = LogFactory.getLog(OnResourceCondition.class);
class OnResourceCondition extends SpringBootCondition {
private ResourceLoader defaultResourceLoader = new DefaultResourceLoader();
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String checking = ConditionLogUtils.getPrefix(logger, metadata);
public Outcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(
ConditionalOnResource.class.getName(), true);
if (attributes != null) {
ResourceLoader loader = context.getResourceLoader() == null ? this.defaultResourceLoader
: context.getResourceLoader();
if (attributes != null) {
List<String> locations = new ArrayList<String>();
collectValues(locations, attributes.get("resources"));
Assert.isTrue(locations.size() > 0,
"@ConditionalOnResource annotations must specify at least one resource location");
for (String location : locations) {
if (logger.isDebugEnabled()) {
logger.debug(checking + "Checking for resource: " + location);
}
if (!loader.getResource(location).exists()) {
if (logger.isDebugEnabled()) {
logger.debug(checking + "Resource not found: " + location
+ " (search terminated with matches=false)");
}
return false;
}
return Outcome.noMatch("resource not found: " + location);
}
}
if (logger.isDebugEnabled()) {
logger.debug(checking + "Match result is: true");
}
return true;
return Outcome.match();
}
private void collectValues(List<String> names, List<Object> values) {
......
......@@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.condition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
......@@ -34,51 +32,46 @@ import org.springframework.web.context.support.StandardServletEnvironment;
* @see ConditionalOnWebApplication
* @see ConditionalOnNotWebApplication
*/
class OnWebApplicationCondition implements Condition {
class OnWebApplicationCondition extends SpringBootCondition {
private static final String WEB_CONTEXT_CLASS = "org.springframework.web.context.support.GenericWebApplicationContext";
private static Log logger = LogFactory.getLog(OnWebApplicationCondition.class);
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean webContextRequired = metadata
public Outcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
boolean webApplicationRequired = metadata
.isAnnotated(ConditionalOnWebApplication.class.getName());
boolean webApplication = isWebApplication(context, metadata);
return (webContextRequired ? webApplication : !webApplication);
Outcome webApplication = isWebApplication(context, metadata);
if (webApplicationRequired && !webApplication.isMatch()) {
return Outcome.noMatch(webApplication.getMessage());
}
private boolean isWebApplication(ConditionContext context,
if (!webApplicationRequired && webApplication.isMatch()) {
return Outcome.noMatch(webApplication.getMessage());
}
return Outcome.match(webApplication.getMessage());
}
private Outcome isWebApplication(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String checking = ConditionLogUtils.getPrefix(logger, metadata);
if (!ClassUtils.isPresent(WEB_CONTEXT_CLASS, context.getClassLoader())) {
if (logger.isDebugEnabled()) {
logger.debug(checking + "web application classes not found");
}
return false;
return Outcome.noMatch("web application classes not found");
}
if (context.getBeanFactory() != null) {
String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
if (ObjectUtils.containsElement(scopes, "session")) {
if (logger.isDebugEnabled()) {
logger.debug(checking + "found web application scope");
}
return true;
return Outcome.match("found web application 'session' scope");
}
}
if (context.getEnvironment() instanceof StandardServletEnvironment) {
if (logger.isDebugEnabled()) {
logger.debug(checking + "found web application environment");
}
return true;
return Outcome.match("found web application StandardServletEnvironment");
}
if (logger.isDebugEnabled()) {
logger.debug(checking + "is not a web application");
}
return false;
return Outcome.noMatch("not a web application");
}
}
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.condition;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Base of all {@link Condition} implementations used with Spring Boot. Provides sensible
* logging to help the user diagnose what classes are loaded.
*
* @author Phillip Webb
*/
public abstract class SpringBootCondition implements Condition {
private final Log logger = LogFactory.getLog(getClass());
@Override
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Outcome result = getMatchOutcome(context, metadata);
if (!result.isMatch()) {
// Log non-matching conditions at debug
if (this.logger.isDebugEnabled()) {
this.logger.debug(getMessage(metadata, result));
}
return false;
}
// Log matching conditions at trace
if (this.logger.isTraceEnabled()) {
this.logger.trace(getMessage(metadata, result));
}
return true;
}
private StringBuilder getMessage(AnnotatedTypeMetadata metadata, Outcome result) {
StringBuilder message = new StringBuilder();
message.append("Condition ");
message.append(ClassUtils.getShortName(getClass()));
message.append(" on ");
if (metadata instanceof ClassMetadata) {
ClassMetadata classMetadata = (ClassMetadata) metadata;
message.append(classMetadata.getClassName());
}
else if (metadata instanceof MethodMetadata) {
MethodMetadata methodMetadata = (MethodMetadata) metadata;
message.append(methodMetadata.getDeclaringClassName());
message.append("#");
message.append(methodMetadata.getMethodName());
}
message.append(result.isMatch() ? " matched" : "did not match");
if (StringUtils.hasLength(result.getMessage())) {
message.append(" due to ");
message.append(result.getMessage());
}
return message;
}
/**
* Determine the outcome of the match along with suitable log output.
*/
public abstract Outcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata);
protected final boolean anyMatches(ConditionContext context,
AnnotatedTypeMetadata metadata, Condition... conditions) {
for (Condition condition : conditions) {
if (matches(context, metadata, condition)) {
return true;
}
}
return false;
}
protected final boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata, Condition condition) {
if (condition instanceof SpringBootCondition) {
return ((SpringBootCondition) condition).getMatchOutcome(context, metadata)
.isMatch();
}
return condition.matches(context, metadata);
}
/**
* Outcome for a match, including log message.
*/
protected final static class Outcome {
private final boolean match;
private final String message;
public Outcome(boolean match, String message) {
this.match = match;
this.message = message;
}
public boolean isMatch() {
return this.match;
}
public String getMessage() {
return this.message;
}
public static Outcome match() {
return match(null);
}
public static Outcome match(String message) {
return new Outcome(true, message);
}
public static Outcome noMatch(String message) {
return new Outcome(false, message);
}
}
}
......@@ -21,8 +21,6 @@ import java.util.Arrays;
import javax.servlet.Servlet;
import org.apache.catalina.startup.Tomcat;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.Loader;
import org.springframework.beans.BeansException;
......@@ -33,10 +31,10 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionLogUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration.EmbeddedServletContainerCustomizerBeanPostProcessorRegistrar;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
......@@ -44,7 +42,6 @@ import org.springframework.boot.context.embedded.ServletContextInitializer;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
......@@ -149,42 +146,24 @@ public class EmbeddedServletContainerAutoConfiguration {
}
}
private static class DefaultServletCondition implements Condition {
private static Log logger = LogFactory.getLog(DefaultServletCondition.class);
private static class DefaultServletCondition extends SpringBootCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String checking = ConditionLogUtils.getPrefix(logger, metadata);
public Outcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] beans = beanFactory.getBeanNamesForType(DispatcherServlet.class,
false, false);
if (beans.length == 0) {
if (logger.isDebugEnabled()) {
logger.debug(checking
+ "No DispatcherServlet found (search terminated with matches=true)");
}
// No dispatcher servlet so no need to ask further questions
return true;
return Outcome.match("no DispatcherServlet found");
}
if (Arrays.asList(beans).contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
if (logger.isDebugEnabled()) {
logger.debug(checking + "DispatcherServlet found and named "
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME
+ " (search terminated with matches=false)");
}
// An existing bean with the default name
return false;
}
if (logger.isDebugEnabled()) {
logger.debug(checking
+ "Multiple DispatcherServlets found and none is named "
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME
+ " (search terminated with matches=true)");
return Outcome.noMatch("found DispatcherServlet named "
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
}
return true;
return Outcome.match("multiple DispatcherServlets found and none is named "
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment