Commit 7396ccfe authored by Phillip Webb's avatar Phillip Webb

Harmonize ConditionOutcome messages

Add ConditionMessage class to help build condition messages in a
uniform format and update existing conditions to use it.

Fixes gh-6756
parent 41dc53f5
...@@ -26,6 +26,7 @@ import org.springframework.boot.actuate.endpoint.Endpoint; ...@@ -26,6 +26,7 @@ import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter; import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
...@@ -92,8 +93,13 @@ public class EndpointMBeanExportAutoConfiguration { ...@@ -92,8 +93,13 @@ public class EndpointMBeanExportAutoConfiguration {
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
boolean jmxEnabled = isEnabled(context, "spring.jmx."); boolean jmxEnabled = isEnabled(context, "spring.jmx.");
boolean jmxEndpointsEnabled = isEnabled(context, "endpoints.jmx."); boolean jmxEndpointsEnabled = isEnabled(context, "endpoints.jmx.");
return new ConditionOutcome(jmxEnabled && jmxEndpointsEnabled, if (jmxEnabled && jmxEndpointsEnabled) {
"JMX Endpoints"); return ConditionOutcome.match(
ConditionMessage.forCondition("JMX Enabled").found("properties")
.items("spring.jmx.enabled", "endpoints.jmx.enabled"));
}
return ConditionOutcome.noMatch(ConditionMessage.forCondition("JMX Enabled")
.because("spring.jmx.enabled or endpoints.jmx.enabled is not set"));
} }
private boolean isEnabled(ConditionContext context, String prefix) { private boolean isEnabled(ConditionContext context, String prefix) {
......
...@@ -40,6 +40,7 @@ import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext; ...@@ -40,6 +40,7 @@ import org.springframework.boot.actuate.endpoint.mvc.ManagementServletContext;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
...@@ -333,13 +334,18 @@ public class EndpointWebMvcAutoConfiguration ...@@ -333,13 +334,18 @@ public class EndpointWebMvcAutoConfiguration
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("Management Server MVC");
if (!(context.getResourceLoader() instanceof WebApplicationContext)) { if (!(context.getResourceLoader() instanceof WebApplicationContext)) {
return ConditionOutcome.noMatch("Non WebApplicationContext"); return ConditionOutcome
.noMatch(message.because("non WebApplicationContext"));
} }
ManagementServerPort port = ManagementServerPort.get(context.getEnvironment(), ManagementServerPort port = ManagementServerPort.get(context.getEnvironment(),
context.getBeanFactory()); context.getBeanFactory());
return new ConditionOutcome(port == ManagementServerPort.SAME, if (port == ManagementServerPort.SAME) {
"Management context"); return ConditionOutcome.match(message.because("port is same"));
}
return ConditionOutcome.noMatch(message.because("port is not same"));
} }
} }
......
...@@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint; ...@@ -37,6 +37,7 @@ import org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints; import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoints;
import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint; import org.springframework.boot.actuate.endpoint.mvc.ShutdownMvcEndpoint;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
...@@ -182,20 +183,22 @@ public class EndpointWebMvcManagementContextConfiguration { ...@@ -182,20 +183,22 @@ public class EndpointWebMvcManagementContextConfiguration {
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment(); Environment environment = context.getEnvironment();
String config = environment.resolvePlaceholders("${logging.file:}"); String config = environment.resolvePlaceholders("${logging.file:}");
ConditionMessage.Builder message = ConditionMessage
.forCondition("Log File");
if (StringUtils.hasText(config)) { if (StringUtils.hasText(config)) {
return ConditionOutcome.match("Found logging.file: " + config); return ConditionOutcome.match(message.found("logging.file").items(config));
} }
config = environment.resolvePlaceholders("${logging.path:}"); config = environment.resolvePlaceholders("${logging.path:}");
if (StringUtils.hasText(config)) { if (StringUtils.hasText(config)) {
return ConditionOutcome.match("Found logging.path: " + config); return ConditionOutcome.match(message.found("logging.path").items(config));
} }
config = new RelaxedPropertyResolver(environment, "endpoints.logfile.") config = new RelaxedPropertyResolver(environment, "endpoints.logfile.")
.getProperty("external-file"); .getProperty("external-file");
if (StringUtils.hasText(config)) { if (StringUtils.hasText(config)) {
return ConditionOutcome return ConditionOutcome.match(
.match("Found endpoints.logfile.external-file: " + config); message.found("endpoints.logfile.external-file").items(config));
} }
return ConditionOutcome.noMatch("Found no log file configuration"); return ConditionOutcome.noMatch(message.didNotFind("logging file").atAll());
} }
} }
......
...@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint; ...@@ -25,6 +25,7 @@ import org.springframework.boot.actuate.endpoint.mvc.JolokiaMvcEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
...@@ -97,8 +98,12 @@ public class JolokiaAutoConfiguration { ...@@ -97,8 +98,12 @@ public class JolokiaAutoConfiguration {
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
boolean endpointsEnabled = isEnabled(context, "endpoints.", true); boolean endpointsEnabled = isEnabled(context, "endpoints.", true);
boolean enabled = isEnabled(context, "endpoints.jolokia.", endpointsEnabled); ConditionMessage.Builder message = ConditionMessage
return new ConditionOutcome(enabled, "Jolokia enabled"); .forCondition("Jolokia");
if (isEnabled(context, "endpoints.jolokia.", endpointsEnabled)) {
return ConditionOutcome.match(message.because("enabled"));
}
return ConditionOutcome.noMatch(message.because("not enabled"));
} }
private boolean isEnabled(ConditionContext context, String prefix, private boolean isEnabled(ConditionContext context, String prefix,
......
...@@ -33,6 +33,7 @@ import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint; ...@@ -33,6 +33,7 @@ import org.springframework.boot.actuate.endpoint.mvc.MvcEndpoint;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
...@@ -223,10 +224,13 @@ public class ManagementWebSecurityAutoConfiguration { ...@@ -223,10 +224,13 @@ public class ManagementWebSecurityAutoConfiguration {
.getProperty("management.security.enabled", "true"); .getProperty("management.security.enabled", "true");
String basicEnabled = context.getEnvironment() String basicEnabled = context.getEnvironment()
.getProperty("security.basic.enabled", "true"); .getProperty("security.basic.enabled", "true");
return new ConditionOutcome( ConditionMessage.Builder message = ConditionMessage
"true".equalsIgnoreCase(managementEnabled) .forCondition("WebSecurityEnabled");
&& !"true".equalsIgnoreCase(basicEnabled), if ("true".equalsIgnoreCase(managementEnabled)
"Management security enabled and basic disabled"); && !"true".equalsIgnoreCase(basicEnabled)) {
return ConditionOutcome.match(message.because("security enabled"));
}
return ConditionOutcome.noMatch(message.because("security disabled"));
} }
} }
......
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.lang.annotation.Annotation;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
...@@ -33,9 +36,10 @@ abstract class OnEnabledEndpointElementCondition extends SpringBootCondition { ...@@ -33,9 +36,10 @@ abstract class OnEnabledEndpointElementCondition extends SpringBootCondition {
private final String prefix; private final String prefix;
private final Class<?> annotationType; private final Class<? extends Annotation> annotationType;
OnEnabledEndpointElementCondition(String prefix, Class<?> annotationType) { OnEnabledEndpointElementCondition(String prefix,
Class<? extends Annotation> annotationType) {
this.prefix = prefix; this.prefix = prefix;
this.annotationType = annotationType; this.annotationType = annotationType;
} }
...@@ -69,7 +73,8 @@ abstract class OnEnabledEndpointElementCondition extends SpringBootCondition { ...@@ -69,7 +73,8 @@ abstract class OnEnabledEndpointElementCondition extends SpringBootCondition {
if (resolver.containsProperty("enabled")) { if (resolver.containsProperty("enabled")) {
boolean match = resolver.getProperty("enabled", Boolean.class, true); boolean match = resolver.getProperty("enabled", Boolean.class, true);
return new ConditionOutcome(match, return new ConditionOutcome(match,
getEndpointElementOutcomeMessage(endpointName, match)); ConditionMessage.forCondition(this.annotationType).because(
this.prefix + endpointName + ".enabled is " + match));
} }
return null; return null;
} }
...@@ -79,7 +84,8 @@ abstract class OnEnabledEndpointElementCondition extends SpringBootCondition { ...@@ -79,7 +84,8 @@ abstract class OnEnabledEndpointElementCondition extends SpringBootCondition {
context.getEnvironment(), this.prefix + "defaults."); context.getEnvironment(), this.prefix + "defaults.");
boolean match = Boolean.valueOf(resolver.getProperty("enabled", "true")); boolean match = Boolean.valueOf(resolver.getProperty("enabled", "true"));
return new ConditionOutcome(match, return new ConditionOutcome(match,
getDefaultEndpointElementOutcomeMessage(match)); ConditionMessage.forCondition(this.annotationType).because(
this.prefix + "defaults.enabled is considered " + match));
} }
} }
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.actuate.condition; package org.springframework.boot.actuate.condition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
...@@ -31,14 +32,11 @@ import org.springframework.core.type.AnnotatedTypeMetadata; ...@@ -31,14 +32,11 @@ import org.springframework.core.type.AnnotatedTypeMetadata;
*/ */
class OnEnabledEndpointCondition extends SpringBootCondition { class OnEnabledEndpointCondition extends SpringBootCondition {
private static final String ANNOTATION_CLASS = ConditionalOnEnabledEndpoint.class
.getName();
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
AnnotationAttributes annotationAttributes = AnnotationAttributes AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(metadata
.fromMap(metadata.getAnnotationAttributes(ANNOTATION_CLASS)); .getAnnotationAttributes(ConditionalOnEnabledEndpoint.class.getName()));
String endpointName = annotationAttributes.getString("value"); String endpointName = annotationAttributes.getString("value");
boolean enabledByDefault = annotationAttributes.getBoolean("enabledByDefault"); boolean enabledByDefault = annotationAttributes.getBoolean("enabledByDefault");
ConditionOutcome outcome = determineEndpointOutcome(endpointName, ConditionOutcome outcome = determineEndpointOutcome(endpointName,
...@@ -56,8 +54,11 @@ class OnEnabledEndpointCondition extends SpringBootCondition { ...@@ -56,8 +54,11 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
if (resolver.containsProperty("enabled") || !enabledByDefault) { if (resolver.containsProperty("enabled") || !enabledByDefault) {
boolean match = resolver.getProperty("enabled", Boolean.class, boolean match = resolver.getProperty("enabled", Boolean.class,
enabledByDefault); enabledByDefault);
return new ConditionOutcome(match, "The endpoint " + endpointName + " is " ConditionMessage message = ConditionMessage
+ (match ? "enabled" : "disabled")); .forCondition(ConditionalOnEnabledEndpoint.class,
"(" + endpointName + ")")
.because(match ? "enabled" : "disabled");
return new ConditionOutcome(match, message);
} }
return null; return null;
} }
...@@ -66,8 +67,11 @@ class OnEnabledEndpointCondition extends SpringBootCondition { ...@@ -66,8 +67,11 @@ class OnEnabledEndpointCondition extends SpringBootCondition {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "endpoints."); context.getEnvironment(), "endpoints.");
boolean match = Boolean.valueOf(resolver.getProperty("enabled", "true")); boolean match = Boolean.valueOf(resolver.getProperty("enabled", "true"));
return new ConditionOutcome(match, ConditionMessage message = ConditionMessage
"All endpoints are " + (match ? "enabled" : "disabled") + " by default"); .forCondition(ConditionalOnEnabledEndpoint.class)
.because("All endpoints are " + (match ? "enabled" : "disabled")
+ " by default");
return new ConditionOutcome(match, message);
} }
} }
...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure; ...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration.ResourceBundleCondition; import org.springframework.boot.autoconfigure.MessageSourceAutoConfiguration.ResourceBundleCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.condition.SearchStrategy;
...@@ -161,17 +162,19 @@ public class MessageSourceAutoConfiguration { ...@@ -161,17 +162,19 @@ public class MessageSourceAutoConfiguration {
private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context, private ConditionOutcome getMatchOutcomeForBasename(ConditionContext context,
String basename) { String basename) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("ResourceBundle");
for (String name : StringUtils.commaDelimitedListToStringArray( for (String name : StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(basename))) { StringUtils.trimAllWhitespace(basename))) {
for (Resource resource : getResources(context.getClassLoader(), name)) { for (Resource resource : getResources(context.getClassLoader(), name)) {
if (resource.exists()) { if (resource.exists()) {
return ConditionOutcome.match("Bundle found for " return ConditionOutcome
+ "spring.messages.basename: " + name); .match(message.found("bundle").items(resource));
} }
} }
} }
return ConditionOutcome.noMatch( return ConditionOutcome.noMatch(
"No bundle found for " + "spring.messages.basename: " + basename); message.didNotFind("bundle with basename " + basename).atAll());
} }
private Resource[] getResources(ClassLoader classLoader, String name) { private Resource[] getResources(ClassLoader classLoader, String name) {
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,12 +16,14 @@ ...@@ -16,12 +16,14 @@
package org.springframework.boot.autoconfigure.cache; package org.springframework.boot.autoconfigure.cache;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
/** /**
* General cache condition used with all cache configuration classes. * General cache condition used with all cache configuration classes.
...@@ -35,18 +37,24 @@ class CacheCondition extends SpringBootCondition { ...@@ -35,18 +37,24 @@ class CacheCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
String sourceClass = "";
if (metadata instanceof ClassMetadata) {
sourceClass = ((ClassMetadata) metadata).getClassName();
}
ConditionMessage.Builder message = ConditionMessage.forCondition("Cache",
sourceClass);
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.cache."); context.getEnvironment(), "spring.cache.");
if (!resolver.containsProperty("type")) { if (!resolver.containsProperty("type")) {
return ConditionOutcome.match("Automatic cache type"); return ConditionOutcome.match(message.because("automatic cache type"));
} }
CacheType cacheType = CacheConfigurations CacheType cacheType = CacheConfigurations
.getType(((AnnotationMetadata) metadata).getClassName()); .getType(((AnnotationMetadata) metadata).getClassName());
String value = resolver.getProperty("type").replace("-", "_").toUpperCase(); String value = resolver.getProperty("type").replace("-", "_").toUpperCase();
if (value.equals(cacheType.name())) { if (value.equals(cacheType.name())) {
return ConditionOutcome.match("Cache type " + cacheType); return ConditionOutcome.match(message.because(value + " cache type"));
} }
return ConditionOutcome.noMatch("Cache type " + value); return ConditionOutcome.noMatch(message.because(value + " cache type"));
} }
} }
...@@ -28,6 +28,7 @@ import javax.cache.spi.CachingProvider; ...@@ -28,6 +28,7 @@ import javax.cache.spi.CachingProvider;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
...@@ -177,23 +178,28 @@ class JCacheCacheConfiguration { ...@@ -177,23 +178,28 @@ class JCacheCacheConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("JCache");
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.cache.jcache."); context.getEnvironment(), "spring.cache.jcache.");
if (resolver.containsProperty("provider")) { if (resolver.containsProperty("provider")) {
return ConditionOutcome.match("JCache provider specified"); return ConditionOutcome
.match(message.because("JCache provider specified"));
} }
Iterator<CachingProvider> providers = Caching.getCachingProviders() Iterator<CachingProvider> providers = Caching.getCachingProviders()
.iterator(); .iterator();
if (!providers.hasNext()) { if (!providers.hasNext()) {
return ConditionOutcome.noMatch("No JSR-107 compliant providers"); return ConditionOutcome
.noMatch(message.didNotFind("JSR-107 provider").atAll());
} }
providers.next(); providers.next();
if (providers.hasNext()) { if (providers.hasNext()) {
return ConditionOutcome.noMatch( return ConditionOutcome
"Multiple default JSR-107 compliant " + "providers found"); .noMatch(message.foundExactly("multiple JSR-107 providers"));
} }
return ConditionOutcome.match("Default JSR-107 compliant provider found."); return ConditionOutcome
.match(message.foundExactly("single JSR-107 provider"));
} }
} }
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -35,7 +35,6 @@ import org.springframework.util.Assert; ...@@ -35,7 +35,6 @@ import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/** /**
* Abstract base class for nested conditions. * Abstract base class for nested conditions.
...@@ -171,16 +170,18 @@ abstract class AbstractNestedCondition extends SpringBootCondition ...@@ -171,16 +170,18 @@ abstract class AbstractNestedCondition extends SpringBootCondition
private ConditionOutcome getConditionOutcome(AnnotationMetadata metadata, private ConditionOutcome getConditionOutcome(AnnotationMetadata metadata,
Condition condition) { Condition condition) {
String messagePrefix = "member condition on " + metadata.getClassName(); String className = ClassUtils.getShortName(metadata.getClassName());
if (condition instanceof SpringBootCondition) { if (condition instanceof SpringBootCondition) {
ConditionOutcome outcome = ((SpringBootCondition) condition) ConditionOutcome outcome = ((SpringBootCondition) condition)
.getMatchOutcome(this.context, metadata); .getMatchOutcome(this.context, metadata);
String message = outcome.getMessage(); ConditionMessage message = outcome.getConditionMessage()
return new ConditionOutcome(outcome.isMatch(), messagePrefix .append("on member " + className);
+ (StringUtils.hasLength(message) ? " : " + message : "")); return new ConditionOutcome(outcome.isMatch(), message);
} }
boolean matches = condition.matches(this.context, metadata); boolean matches = condition.matches(this.context, metadata);
return new ConditionOutcome(matches, messagePrefix); return new ConditionOutcome(matches,
ConditionMessage.forCondition("NestedCondition")
.because("nested on member " + className));
} }
} }
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
package org.springframework.boot.autoconfigure.condition; package org.springframework.boot.autoconfigure.condition;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
/** /**
...@@ -47,11 +50,19 @@ public abstract class AllNestedConditions extends AbstractNestedCondition { ...@@ -47,11 +50,19 @@ public abstract class AllNestedConditions extends AbstractNestedCondition {
@Override @Override
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) { protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
return new ConditionOutcome( boolean match = hasSameSize(memberOutcomes.getMatches(), memberOutcomes.getAll());
memberOutcomes.getMatches().size() == memberOutcomes.getAll().size(), List<ConditionMessage> messages = new ArrayList<ConditionMessage>();
"nested all match resulted in " + memberOutcomes.getMatches() messages.add(ConditionMessage.forCondition("AllNestedConditions")
+ " matches and " + memberOutcomes.getNonMatches() .because(memberOutcomes.getMatches().size() + " matched "
+ " non matches"); + memberOutcomes.getNonMatches().size() + " did not"));
for (ConditionOutcome outcome : memberOutcomes.getAll()) {
messages.add(outcome.getConditionMessage());
}
return new ConditionOutcome(match, ConditionMessage.of(messages));
}
private boolean hasSameSize(List<?> list1, List<?> list2) {
return list1.size() == list2.size();
} }
} }
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
package org.springframework.boot.autoconfigure.condition; package org.springframework.boot.autoconfigure.condition;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
...@@ -50,11 +53,15 @@ public abstract class AnyNestedCondition extends AbstractNestedCondition { ...@@ -50,11 +53,15 @@ public abstract class AnyNestedCondition extends AbstractNestedCondition {
@Override @Override
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) { protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
return new ConditionOutcome(!memberOutcomes.getMatches().isEmpty(), boolean match = !memberOutcomes.getMatches().isEmpty();
"nested any match resulted in " + memberOutcomes.getMatches() List<ConditionMessage> messages = new ArrayList<ConditionMessage>();
+ " matches and " + memberOutcomes.getNonMatches() messages.add(ConditionMessage.forCondition("AnyNestedCondition")
+ " non matches"); .because(memberOutcomes.getMatches().size() + " matched "
+ memberOutcomes.getNonMatches().size() + " did not"));
for (ConditionOutcome outcome : memberOutcomes.getAll()) {
messages.add(outcome.getConditionMessage());
}
return new ConditionOutcome(match, ConditionMessage.of(messages));
} }
} }
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -127,8 +127,8 @@ public final class ConditionEvaluationReport { ...@@ -127,8 +127,8 @@ public final class ConditionEvaluationReport {
String prefix = source + "$"; String prefix = source + "$";
for (Entry<String, ConditionAndOutcomes> entry : this.outcomes.entrySet()) { for (Entry<String, ConditionAndOutcomes> entry : this.outcomes.entrySet()) {
if (entry.getKey().startsWith(prefix)) { if (entry.getKey().startsWith(prefix)) {
ConditionOutcome outcome = new ConditionOutcome(false, ConditionOutcome outcome = ConditionOutcome.noMatch(ConditionMessage
"Ancestor '" + source + "' did not match"); .forCondition("Ancestor " + source).because("did not match"));
entry.getValue().add(ANCESTOR_CONDITION, outcome); entry.getValue().add(ANCESTOR_CONDITION, outcome);
} }
} }
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,20 +16,38 @@ ...@@ -16,20 +16,38 @@
package org.springframework.boot.autoconfigure.condition; package org.springframework.boot.autoconfigure.condition;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
* Outcome for a condition match, including log message. * Outcome for a condition match, including log message.
* *
* @author Phillip Webb * @author Phillip Webb
* @see ConditionMessage
*/ */
public class ConditionOutcome { public class ConditionOutcome {
private final boolean match; private final boolean match;
private final String message; private final ConditionMessage message;
/**
* Create a new {@link ConditionOutcome} instance. For more consistent messages
* consider using {@link #ConditionOutcome(boolean, ConditionMessage)}.
* @param match if the condition is a match
* @param message the condition message
*/
public ConditionOutcome(boolean match, String message) { public ConditionOutcome(boolean match, String message) {
this(match, ConditionMessage.of(message));
}
/**
* Create a new {@link ConditionOutcome} instance.
* @param match if the condition is a match
* @param message the condition message
*/
public ConditionOutcome(boolean match, ConditionMessage message) {
Assert.notNull(message, "ConditionMessage must not be null");
this.match = match; this.match = match;
this.message = message; this.message = message;
} }
...@@ -39,11 +57,12 @@ public class ConditionOutcome { ...@@ -39,11 +57,12 @@ public class ConditionOutcome {
* @return the {@link ConditionOutcome} * @return the {@link ConditionOutcome}
*/ */
public static ConditionOutcome match() { public static ConditionOutcome match() {
return match(null); return match(ConditionMessage.empty());
} }
/** /**
* Create a new {@link ConditionOutcome} instance for 'match'. * Create a new {@link ConditionOutcome} instance for 'match'. For more consistent
* messages consider using {@link #match(ConditionMessage)}.
* @param message the message * @param message the message
* @return the {@link ConditionOutcome} * @return the {@link ConditionOutcome}
*/ */
...@@ -52,7 +71,17 @@ public class ConditionOutcome { ...@@ -52,7 +71,17 @@ public class ConditionOutcome {
} }
/** /**
* Create a new {@link ConditionOutcome} instance for 'no match'. * Create a new {@link ConditionOutcome} instance for 'match'.
* @param message the message
* @return the {@link ConditionOutcome}
*/
public static ConditionOutcome match(ConditionMessage message) {
return new ConditionOutcome(true, message);
}
/**
* Create a new {@link ConditionOutcome} instance for 'no match'. For more consistent
* messages consider using {@link #noMatch(ConditionMessage)}.
* @param message the message * @param message the message
* @return the {@link ConditionOutcome} * @return the {@link ConditionOutcome}
*/ */
...@@ -60,6 +89,15 @@ public class ConditionOutcome { ...@@ -60,6 +89,15 @@ public class ConditionOutcome {
return new ConditionOutcome(false, message); return new ConditionOutcome(false, message);
} }
/**
* Create a new {@link ConditionOutcome} instance for 'no match'.
* @param message the message
* @return the {@link ConditionOutcome}
*/
public static ConditionOutcome noMatch(ConditionMessage message) {
return new ConditionOutcome(false, message);
}
/** /**
* Return {@code true} if the outcome was a match. * Return {@code true} if the outcome was a match.
* @return {@code true} if the outcome matches * @return {@code true} if the outcome matches
...@@ -73,6 +111,14 @@ public class ConditionOutcome { ...@@ -73,6 +111,14 @@ public class ConditionOutcome {
* @return the message or {@code null} * @return the message or {@code null}
*/ */
public String getMessage() { public String getMessage() {
return (this.message.isEmpty() ? null : this.message.toString());
}
/**
* Return an outcome message or {@code null}.
* @return the message or {@code null}
*/
public ConditionMessage getConditionMessage() {
return this.message; return this.message;
} }
...@@ -100,7 +146,7 @@ public class ConditionOutcome { ...@@ -100,7 +146,7 @@ public class ConditionOutcome {
@Override @Override
public String toString() { public String toString() {
return (this.message == null ? "" : this.message); return (this.message == null ? "" : this.message.toString());
} }
/** /**
...@@ -110,7 +156,7 @@ public class ConditionOutcome { ...@@ -110,7 +156,7 @@ public class ConditionOutcome {
* @since 1.3.0 * @since 1.3.0
*/ */
public static ConditionOutcome inverse(ConditionOutcome outcome) { public static ConditionOutcome inverse(ConditionOutcome outcome) {
return new ConditionOutcome(!outcome.isMatch(), outcome.getMessage()); return new ConditionOutcome(!outcome.isMatch(), outcome.getConditionMessage());
} }
} }
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
package org.springframework.boot.autoconfigure.condition; package org.springframework.boot.autoconfigure.condition;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
/** /**
...@@ -47,10 +50,15 @@ public abstract class NoneNestedConditions extends AbstractNestedCondition { ...@@ -47,10 +50,15 @@ public abstract class NoneNestedConditions extends AbstractNestedCondition {
@Override @Override
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) { protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
return new ConditionOutcome(memberOutcomes.getMatches().isEmpty(), boolean match = memberOutcomes.getMatches().isEmpty();
"nested none match resulted in " + memberOutcomes.getMatches() List<ConditionMessage> messages = new ArrayList<ConditionMessage>();
+ " matches and " + memberOutcomes.getNonMatches() messages.add(ConditionMessage.forCondition("NoneNestedConditions")
+ " non matches"); .because(memberOutcomes.getMatches().size() + " matched "
+ memberOutcomes.getNonMatches().size() + " did not"));
for (ConditionOutcome outcome : memberOutcomes.getAll()) {
messages.add(outcome.getConditionMessage());
}
return new ConditionOutcome(match, ConditionMessage.of(messages));
} }
} }
...@@ -31,6 +31,7 @@ import org.springframework.beans.factory.HierarchicalBeanFactory; ...@@ -31,6 +31,7 @@ import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
...@@ -74,49 +75,52 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit ...@@ -74,49 +75,52 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
StringBuilder matchMessage = new StringBuilder(); ConditionMessage matchMessage = ConditionMessage.empty();
if (metadata.isAnnotated(ConditionalOnBean.class.getName())) { if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
BeanSearchSpec spec = new BeanSearchSpec(context, metadata, BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
ConditionalOnBean.class); ConditionalOnBean.class);
List<String> matching = getMatchingBeans(context, spec); List<String> matching = getMatchingBeans(context, spec);
if (matching.isEmpty()) { if (matching.isEmpty()) {
return ConditionOutcome return ConditionOutcome.noMatch(
.noMatch("@ConditionalOnBean " + spec + " found no beans"); ConditionMessage.forCondition(ConditionalOnBean.class, spec)
.didNotFind("any beans").atAll());
} }
matchMessage.append("@ConditionalOnBean ").append(spec) matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec)
.append(" found the following ").append(matching); .found("bean", "beans").items(Style.QUOTE, matching);
} }
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) { if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata, BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
ConditionalOnSingleCandidate.class); ConditionalOnSingleCandidate.class);
List<String> matching = getMatchingBeans(context, spec); List<String> matching = getMatchingBeans(context, spec);
if (matching.isEmpty()) { if (matching.isEmpty()) {
return ConditionOutcome.noMatch( return ConditionOutcome.noMatch(ConditionMessage
"@ConditionalOnSingleCandidate " + spec + " found no beans"); .forCondition(ConditionalOnSingleCandidate.class, spec)
.didNotFind("any beans").atAll());
} }
else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matching, else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matching,
spec.getStrategy() == SearchStrategy.ALL)) { spec.getStrategy() == SearchStrategy.ALL)) {
return ConditionOutcome.noMatch("@ConditionalOnSingleCandidate " + spec return ConditionOutcome.noMatch(ConditionMessage
+ " found no primary candidate amongst the" + " following " .forCondition(ConditionalOnSingleCandidate.class, spec)
+ matching); .didNotFind("a primary bean from beans")
.items(Style.QUOTE, matching));
} }
matchMessage.append("@ConditionalOnSingleCandidate ").append(spec) matchMessage = matchMessage
.append(" found a primary candidate amongst the following ") .andCondition(ConditionalOnSingleCandidate.class, spec)
.append(matching); .found("a primary bean from beans").items(Style.QUOTE, matching);
} }
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) { if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
BeanSearchSpec spec = new BeanSearchSpec(context, metadata, BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
ConditionalOnMissingBean.class); ConditionalOnMissingBean.class);
List<String> matching = getMatchingBeans(context, spec); List<String> matching = getMatchingBeans(context, spec);
if (!matching.isEmpty()) { if (!matching.isEmpty()) {
return ConditionOutcome.noMatch("@ConditionalOnMissingBean " + spec return ConditionOutcome.noMatch(ConditionMessage
+ " found the following " + matching); .forCondition(ConditionalOnMissingBean.class, spec)
.found("bean", "beans").items(Style.QUOTE, matching));
} }
matchMessage.append(matchMessage.length() == 0 ? "" : " "); matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec)
matchMessage.append("@ConditionalOnMissingBean ").append(spec) .didNotFind("any beans").atAll();
.append(" found no beans");
} }
return ConditionOutcome.match(matchMessage.toString()); return ConditionOutcome.match(matchMessage);
} }
private List<String> getMatchingBeans(ConditionContext context, private List<String> getMatchingBeans(ConditionContext context,
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -21,6 +21,7 @@ import java.util.Iterator; ...@@ -21,6 +21,7 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
...@@ -28,7 +29,6 @@ import org.springframework.core.annotation.Order; ...@@ -28,7 +29,6 @@ import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/** /**
* {@link Condition} that checks for the presence or absence of specific classes. * {@link Condition} that checks for the presence or absence of specific classes.
...@@ -43,9 +43,7 @@ class OnClassCondition extends SpringBootCondition { ...@@ -43,9 +43,7 @@ class OnClassCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
StringBuilder matchMessage = new StringBuilder();
MultiValueMap<String, Object> onClasses = getAttributes(metadata, MultiValueMap<String, Object> onClasses = getAttributes(metadata,
ConditionalOnClass.class); ConditionalOnClass.class);
if (onClasses != null) { if (onClasses != null) {
...@@ -53,32 +51,31 @@ class OnClassCondition extends SpringBootCondition { ...@@ -53,32 +51,31 @@ class OnClassCondition extends SpringBootCondition {
context); context);
if (!missing.isEmpty()) { if (!missing.isEmpty()) {
return ConditionOutcome return ConditionOutcome
.noMatch("required @ConditionalOnClass classes not found: " .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
+ StringUtils.collectionToCommaDelimitedString(missing)); .didNotFind("required class", "required classes")
.items(Style.QUOTE, missing));
} }
matchMessage.append("@ConditionalOnClass classes found: ") matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.append(StringUtils.collectionToCommaDelimitedString( .found("required class", "required classes").items(Style.QUOTE,
getMatchingClasses(onClasses, MatchType.PRESENT, context))); getMatchingClasses(onClasses, MatchType.PRESENT, context));
} }
MultiValueMap<String, Object> onMissingClasses = getAttributes(metadata, MultiValueMap<String, Object> onMissingClasses = getAttributes(metadata,
ConditionalOnMissingClass.class); ConditionalOnMissingClass.class);
if (onMissingClasses != null) { if (onMissingClasses != null) {
List<String> present = getMatchingClasses(onMissingClasses, MatchType.PRESENT, List<String> present = getMatchingClasses(onMissingClasses, MatchType.PRESENT,
context); context);
if (!present.isEmpty()) { if (!present.isEmpty()) {
return ConditionOutcome return ConditionOutcome.noMatch(
.noMatch("required @ConditionalOnMissing classes found: " ConditionMessage.forCondition(ConditionalOnMissingClass.class)
+ StringUtils.collectionToCommaDelimitedString(present)); .found("unwanted class", "unwanted classes")
.items(Style.QUOTE, present));
} }
matchMessage.append(matchMessage.length() == 0 ? "" : " "); matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
matchMessage.append("@ConditionalOnMissing classes not found: ") .didNotFind("unwanted class", "unwanted classes")
.append(StringUtils.collectionToCommaDelimitedString( .items(Style.QUOTE, getMatchingClasses(onMissingClasses,
getMatchingClasses(onMissingClasses, MatchType.MISSING, MatchType.MISSING, context));
context)));
} }
return ConditionOutcome.match(matchMessage);
return ConditionOutcome.match(matchMessage.toString());
} }
private MultiValueMap<String, Object> getAttributes(AnnotatedTypeMetadata metadata, private MultiValueMap<String, Object> getAttributes(AnnotatedTypeMetadata metadata,
...@@ -111,17 +108,21 @@ class OnClassCondition extends SpringBootCondition { ...@@ -111,17 +108,21 @@ class OnClassCondition extends SpringBootCondition {
private enum MatchType { private enum MatchType {
PRESENT { PRESENT {
@Override @Override
public boolean matches(String className, ConditionContext context) { public boolean matches(String className, ConditionContext context) {
return ClassUtils.isPresent(className, context.getClassLoader()); return ClassUtils.isPresent(className, context.getClassLoader());
} }
}, },
MISSING { MISSING {
@Override @Override
public boolean matches(String className, ConditionContext context) { public boolean matches(String className, ConditionContext context) {
return !ClassUtils.isPresent(className, context.getClassLoader()); return !ClassUtils.isPresent(className, context.getClassLoader());
} }
}; };
public abstract boolean matches(String className, ConditionContext context); public abstract boolean matches(String className, ConditionContext context);
......
/* /*
* Copyright 2012-2013 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -24,7 +24,6 @@ import org.springframework.context.expression.StandardBeanExpressionResolver; ...@@ -24,7 +24,6 @@ import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.core.Ordered; import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.ClassMetadata;
/** /**
* A Condition that evaluates a SpEL expression. * A Condition that evaluates a SpEL expression.
...@@ -38,17 +37,11 @@ class OnExpressionCondition extends SpringBootCondition { ...@@ -38,17 +37,11 @@ class OnExpressionCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
String expression = (String) metadata String expression = (String) metadata
.getAnnotationAttributes(ConditionalOnExpression.class.getName()) .getAnnotationAttributes(ConditionalOnExpression.class.getName())
.get("value"); .get("value");
expression = wrapIfNecessary(expression);
String rawExpression = expression; String rawExpression = expression;
if (!expression.startsWith("#{")) {
// For convenience allow user to provide bare expression with no #{} wrapper
expression = "#{" + expression + "}";
}
// Explicitly allow environment placeholders inside the expression
expression = context.getEnvironment().resolvePlaceholders(expression); expression = context.getEnvironment().resolvePlaceholders(expression);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
BeanExpressionResolver resolver = (beanFactory != null) BeanExpressionResolver resolver = (beanFactory != null)
...@@ -59,13 +52,21 @@ class OnExpressionCondition extends SpringBootCondition { ...@@ -59,13 +52,21 @@ class OnExpressionCondition extends SpringBootCondition {
resolver = new StandardBeanExpressionResolver(); resolver = new StandardBeanExpressionResolver();
} }
boolean result = (Boolean) resolver.evaluate(expression, expressionContext); boolean result = (Boolean) resolver.evaluate(expression, expressionContext);
return new ConditionOutcome(result, ConditionMessage
.forCondition(ConditionalOnExpression.class, "(" + rawExpression + ")")
.resultedIn(result));
}
StringBuilder message = new StringBuilder("SpEL expression"); /**
if (metadata instanceof ClassMetadata) { * Allow user to provide bare expression with no '#{}' wrapper.
message.append(" on " + ((ClassMetadata) metadata).getClassName()); * @param expression source expression
* @return wrapped expression
*/
private String wrapIfNecessary(String expression) {
if (!expression.startsWith("#{")) {
return "#{" + expression + "}";
} }
message.append(": " + rawExpression); return expression;
return new ConditionOutcome(result, message.toString());
} }
} }
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -52,13 +52,13 @@ class OnJavaCondition extends SpringBootCondition { ...@@ -52,13 +52,13 @@ class OnJavaCondition extends SpringBootCondition {
protected ConditionOutcome getMatchOutcome(Range range, JavaVersion runningVersion, protected ConditionOutcome getMatchOutcome(Range range, JavaVersion runningVersion,
JavaVersion version) { JavaVersion version) {
boolean match = runningVersion.isWithin(range, version); boolean match = runningVersion.isWithin(range, version);
return new ConditionOutcome(match, getMessage(range, runningVersion, version));
}
private String getMessage(Range range, JavaVersion runningVersion,
JavaVersion version) {
String expected = String.format( String expected = String.format(
range == Range.EQUAL_OR_NEWER ? "%s or newer" : "older than %s", version); range == Range.EQUAL_OR_NEWER ? "(%s or newer)" : "(older than %s)",
return "Required JVM version " + expected + " found " + runningVersion; version);
ConditionMessage message = ConditionMessage
.forCondition(ConditionalOnJava.class, expected)
.foundExactly(runningVersion);
return new ConditionOutcome(match, message);
} }
} }
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -48,26 +48,33 @@ class OnJndiCondition extends SpringBootCondition { ...@@ -48,26 +48,33 @@ class OnJndiCondition extends SpringBootCondition {
return getMatchOutcome(locations); return getMatchOutcome(locations);
} }
catch (NoClassDefFoundError ex) { catch (NoClassDefFoundError ex) {
return ConditionOutcome.noMatch("JNDI class not found"); return ConditionOutcome
.noMatch(ConditionMessage.forCondition(ConditionalOnJndi.class)
.because("JNDI class not found"));
} }
} }
private ConditionOutcome getMatchOutcome(String[] locations) { private ConditionOutcome getMatchOutcome(String[] locations) {
if (!isJndiAvailable()) { if (!isJndiAvailable()) {
return ConditionOutcome.noMatch("JNDI environment is not available"); return ConditionOutcome
.noMatch(ConditionMessage.forCondition(ConditionalOnJndi.class)
.notAvailable("JNDI environment"));
} }
if (locations.length == 0) { if (locations.length == 0) {
return ConditionOutcome.match("JNDI environment is available"); return ConditionOutcome.match(ConditionMessage
.forCondition(ConditionalOnJndi.class).available("JNDI environment"));
} }
JndiLocator locator = getJndiLocator(locations); JndiLocator locator = getJndiLocator(locations);
String location = locator.lookupFirstLocation(); String location = locator.lookupFirstLocation();
String details = "(" + StringUtils.arrayToCommaDelimitedString(locations) + ")";
if (location != null) { if (location != null) {
return ConditionOutcome return ConditionOutcome
.match("JNDI location '" + location + "' found from candidates " .match(ConditionMessage.forCondition(ConditionalOnJndi.class, details)
+ StringUtils.arrayToCommaDelimitedString(locations)); .foundExactly("\"" + location + "\""));
} }
return ConditionOutcome.noMatch("No JNDI location found from candidates " return ConditionOutcome
+ StringUtils.arrayToCommaDelimitedString(locations)); .noMatch(ConditionMessage.forCondition(ConditionalOnJndi.class, details)
.didNotFind("any matching JNDI location").atAll());
} }
protected boolean isJndiAvailable() { protected boolean isJndiAvailable() {
......
...@@ -22,6 +22,7 @@ import java.util.List; ...@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
...@@ -50,12 +51,17 @@ class OnPropertyCondition extends SpringBootCondition { ...@@ -50,12 +51,17 @@ class OnPropertyCondition extends SpringBootCondition {
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap( List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes( metadata.getAllAnnotationAttributes(
ConditionalOnProperty.class.getName())); ConditionalOnProperty.class.getName()));
List<ConditionOutcome> noMatchOutcomes = findNoMatchOutcomes( List<ConditionMessage> noMatch = new ArrayList<ConditionMessage>();
allAnnotationAttributes, context.getEnvironment()); List<ConditionMessage> match = new ArrayList<ConditionMessage>();
if (noMatchOutcomes.isEmpty()) { for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
return ConditionOutcome.match(); ConditionOutcome outcome = determineOutcome(annotationAttributes,
context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
} }
return ConditionOutcome.noMatch(getCompositeMessage(noMatchOutcomes)); return ConditionOutcome.match(ConditionMessage.of(match));
} }
private List<AnnotationAttributes> annotationAttributesFromMultiValueMap( private List<AnnotationAttributes> annotationAttributesFromMultiValueMap(
...@@ -82,104 +88,110 @@ class OnPropertyCondition extends SpringBootCondition { ...@@ -82,104 +88,110 @@ class OnPropertyCondition extends SpringBootCondition {
return annotationAttributes; return annotationAttributes;
} }
private List<ConditionOutcome> findNoMatchOutcomes(
List<AnnotationAttributes> allAnnotationAttributes,
PropertyResolver resolver) {
List<ConditionOutcome> noMatchOutcomes = new ArrayList<ConditionOutcome>(
allAnnotationAttributes.size());
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
ConditionOutcome outcome = determineOutcome(annotationAttributes, resolver);
if (!outcome.isMatch()) {
noMatchOutcomes.add(outcome);
}
}
return noMatchOutcomes;
}
private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes,
PropertyResolver resolver) { PropertyResolver resolver) {
String prefix = annotationAttributes.getString("prefix").trim(); Spec spec = new Spec(annotationAttributes);
if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
prefix = prefix + ".";
}
String havingValue = annotationAttributes.getString("havingValue");
String[] names = getNames(annotationAttributes);
boolean relaxedNames = annotationAttributes.getBoolean("relaxedNames");
boolean matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
if (relaxedNames) {
resolver = new RelaxedPropertyResolver(resolver, prefix);
}
List<String> missingProperties = new ArrayList<String>(); List<String> missingProperties = new ArrayList<String>();
List<String> nonMatchingProperties = new ArrayList<String>(); List<String> nonMatchingProperties = new ArrayList<String>();
for (String name : names) { spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
String key = (relaxedNames ? name : prefix + name);
if (resolver.containsProperty(key)) {
if (!isMatch(resolver.getProperty(key), havingValue)) {
nonMatchingProperties.add(name);
}
}
else {
if (!matchIfMissing) {
missingProperties.add(name);
}
}
}
if (missingProperties.isEmpty() && nonMatchingProperties.isEmpty()) {
return ConditionOutcome.match();
}
StringBuilder message = new StringBuilder("@ConditionalOnProperty ");
if (!missingProperties.isEmpty()) { if (!missingProperties.isEmpty()) {
message.append("missing required properties ") return ConditionOutcome.noMatch(
.append(expandNames(prefix, missingProperties)).append(" "); ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.didNotFind("property", "properties")
.items(Style.QUOTE, missingProperties));
} }
if (!nonMatchingProperties.isEmpty()) { if (!nonMatchingProperties.isEmpty()) {
String expected = StringUtils.hasLength(havingValue) ? havingValue : "!false"; return ConditionOutcome.noMatch(
message.append("expected '").append(expected).append("' for properties ") ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.append(expandNames(prefix, nonMatchingProperties)); .found("different value in property",
"different value in properties")
.items(Style.QUOTE, nonMatchingProperties));
} }
return ConditionOutcome.noMatch(message.toString()); return ConditionOutcome.match(ConditionMessage
.forCondition(ConditionalOnProperty.class, spec).because("matched"));
} }
private String[] getNames(Map<String, Object> annotationAttributes) { private static class Spec {
String[] value = (String[]) annotationAttributes.get("value");
String[] name = (String[]) annotationAttributes.get("name");
Assert.state(value.length > 0 || name.length > 0,
"The name or value attribute of @ConditionalOnProperty must be specified");
Assert.state(value.length == 0 || name.length == 0,
"The name and value attributes of @ConditionalOnProperty are exclusive");
return (value.length > 0 ? value : name);
}
private boolean isMatch(String value, String requiredValue) { private final String prefix;
if (StringUtils.hasLength(requiredValue)) {
return requiredValue.equalsIgnoreCase(value); private final String havingValue;
private final String[] names;
private final boolean relaxedNames;
private final boolean matchIfMissing;
Spec(AnnotationAttributes annotationAttributes) {
String prefix = annotationAttributes.getString("prefix").trim();
if (StringUtils.hasText(prefix) && !prefix.endsWith(".")) {
prefix = prefix + ".";
}
this.prefix = prefix;
this.havingValue = annotationAttributes.getString("havingValue");
this.names = getNames(annotationAttributes);
this.relaxedNames = annotationAttributes.getBoolean("relaxedNames");
this.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
} }
return !"false".equalsIgnoreCase(value);
}
private String expandNames(String prefix, List<String> names) { private String[] getNames(Map<String, Object> annotationAttributes) {
StringBuilder expanded = new StringBuilder(); String[] value = (String[]) annotationAttributes.get("value");
for (String name : names) { String[] name = (String[]) annotationAttributes.get("name");
expanded.append(expanded.length() == 0 ? "" : ", "); Assert.state(value.length > 0 || name.length > 0,
expanded.append(prefix); "The name or value attribute of @ConditionalOnProperty must be specified");
expanded.append(name); Assert.state(value.length == 0 || name.length == 0,
"The name and value attributes of @ConditionalOnProperty are exclusive");
return (value.length > 0 ? value : name);
}
private void collectProperties(PropertyResolver resolver, List<String> missing,
List<String> nonMatching) {
if (this.relaxedNames) {
resolver = new RelaxedPropertyResolver(resolver, this.prefix);
}
for (String name : this.names) {
String key = (this.relaxedNames ? name : this.prefix + name);
if (resolver.containsProperty(key)) {
if (!isMatch(resolver.getProperty(key), this.havingValue)) {
nonMatching.add(name);
}
}
else {
if (!this.matchIfMissing) {
missing.add(name);
}
}
}
} }
return expanded.toString();
}
private String getCompositeMessage(List<ConditionOutcome> noMatchOutcomes) { private boolean isMatch(String value, String requiredValue) {
StringBuilder message = new StringBuilder(); if (StringUtils.hasLength(requiredValue)) {
for (ConditionOutcome noMatchOutcome : noMatchOutcomes) { return requiredValue.equalsIgnoreCase(value);
if (message.length() > 0) {
message.append(". ");
} }
message.append(noMatchOutcome.getMessage().trim()); return !"false".equalsIgnoreCase(value);
} }
return message.toString();
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("(");
result.append(this.prefix);
if (this.names.length == 1) {
result.append(this.names[0]);
}
else {
result.append("[");
result.append(StringUtils.arrayToCommaDelimitedString(this.names));
result.append("]");
}
if (StringUtils.hasLength(this.havingValue)) {
result.append("=").append(this.havingValue);
}
result.append(")");
return result.toString();
}
} }
} }
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.condition; ...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.condition;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.DefaultResourceLoader;
...@@ -42,23 +43,28 @@ class OnResourceCondition extends SpringBootCondition { ...@@ -42,23 +43,28 @@ class OnResourceCondition extends SpringBootCondition {
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attributes = metadata MultiValueMap<String, Object> attributes = metadata
.getAllAnnotationAttributes(ConditionalOnResource.class.getName(), true); .getAllAnnotationAttributes(ConditionalOnResource.class.getName(), true);
if (attributes != null) { ResourceLoader loader = context.getResourceLoader() == null
ResourceLoader loader = context.getResourceLoader() == null ? this.defaultResourceLoader : context.getResourceLoader();
? this.defaultResourceLoader : context.getResourceLoader(); List<String> locations = new ArrayList<String>();
List<String> locations = new ArrayList<String>(); collectValues(locations, attributes.get("resources"));
collectValues(locations, attributes.get("resources")); Assert.isTrue(!locations.isEmpty(),
Assert.isTrue(!locations.isEmpty(), "@ConditionalOnResource annotations must specify at "
"@ConditionalOnResource annotations must specify at least one resource location"); + "least one resource location");
for (String location : locations) { List<String> missing = new ArrayList<String>();
if (!loader for (String location : locations) {
.getResource( String resouce = context.getEnvironment().resolvePlaceholders(location);
context.getEnvironment().resolvePlaceholders(location)) if (!loader.getResource(resouce).exists()) {
.exists()) { missing.add(location);
return ConditionOutcome.noMatch("resource not found: " + location);
}
} }
} }
return ConditionOutcome.match(); if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage
.forCondition(ConditionalOnResource.class)
.didNotFind("resource", "resources").items(Style.QUOTE, missing));
}
return ConditionOutcome
.match(ConditionMessage.forCondition(ConditionalOnResource.class)
.found("location", "locations").items(locations));
} }
private void collectValues(List<String> names, List<Object> values) { private void collectValues(List<String> names, List<Object> values) {
......
/* /*
* Copyright 2012-2014 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -43,45 +43,40 @@ class OnWebApplicationCondition extends SpringBootCondition { ...@@ -43,45 +43,40 @@ class OnWebApplicationCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
boolean webApplicationRequired = metadata boolean required = metadata
.isAnnotated(ConditionalOnWebApplication.class.getName()); .isAnnotated(ConditionalOnWebApplication.class.getName());
ConditionOutcome webApplication = isWebApplication(context, metadata); ConditionOutcome outcome = isWebApplication(context, metadata, required);
if (required && !outcome.isMatch()) {
if (webApplicationRequired && !webApplication.isMatch()) { return ConditionOutcome.noMatch(outcome.getConditionMessage());
return ConditionOutcome.noMatch(webApplication.getMessage());
} }
if (!required && outcome.isMatch()) {
if (!webApplicationRequired && webApplication.isMatch()) { return ConditionOutcome.noMatch(outcome.getConditionMessage());
return ConditionOutcome.noMatch(webApplication.getMessage());
} }
return ConditionOutcome.match(outcome.getConditionMessage());
return ConditionOutcome.match(webApplication.getMessage());
} }
private ConditionOutcome isWebApplication(ConditionContext context, private ConditionOutcome isWebApplication(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata, boolean required) {
ConditionMessage.Builder message = ConditionMessage.forCondition(
ConditionalOnWebApplication.class, required ? "(required)" : "");
if (!ClassUtils.isPresent(WEB_CONTEXT_CLASS, context.getClassLoader())) { if (!ClassUtils.isPresent(WEB_CONTEXT_CLASS, context.getClassLoader())) {
return ConditionOutcome.noMatch("web application classes not found"); return ConditionOutcome
.noMatch(message.didNotFind("web application classes").atAll());
} }
if (context.getBeanFactory() != null) { if (context.getBeanFactory() != null) {
String[] scopes = context.getBeanFactory().getRegisteredScopeNames(); String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
if (ObjectUtils.containsElement(scopes, "session")) { if (ObjectUtils.containsElement(scopes, "session")) {
return ConditionOutcome.match("found web application 'session' scope"); return ConditionOutcome.match(message.foundExactly("'session' scope"));
} }
} }
if (context.getEnvironment() instanceof StandardServletEnvironment) { if (context.getEnvironment() instanceof StandardServletEnvironment) {
return ConditionOutcome return ConditionOutcome
.match("found web application StandardServletEnvironment"); .match(message.foundExactly("StandardServletEnvironment"));
} }
if (context.getResourceLoader() instanceof WebApplicationContext) { if (context.getResourceLoader() instanceof WebApplicationContext) {
return ConditionOutcome.match("found web application WebApplicationContext"); return ConditionOutcome.match(message.foundExactly("WebApplicationContext"));
} }
return ConditionOutcome.noMatch(message.because("not a web application"));
return ConditionOutcome.noMatch("not a web application");
} }
} }
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,12 @@ ...@@ -16,6 +16,12 @@
package org.springframework.boot.autoconfigure.condition; package org.springframework.boot.autoconfigure.condition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
...@@ -61,8 +67,8 @@ public abstract class ResourceCondition extends SpringBootCondition { ...@@ -61,8 +67,8 @@ public abstract class ResourceCondition extends SpringBootCondition {
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), this.prefix); context.getEnvironment(), this.prefix);
if (resolver.containsProperty(this.propertyName)) { if (resolver.containsProperty(this.propertyName)) {
return ConditionOutcome.match("A '" + this.prefix + this.propertyName + "' " return ConditionOutcome.match(startConditionMessage()
+ "property is specified"); .foundExactly("property " + this.prefix + this.propertyName));
} }
return getResourceOutcome(context, metadata); return getResourceOutcome(context, metadata);
} }
...@@ -75,15 +81,26 @@ public abstract class ResourceCondition extends SpringBootCondition { ...@@ -75,15 +81,26 @@ public abstract class ResourceCondition extends SpringBootCondition {
*/ */
protected ConditionOutcome getResourceOutcome(ConditionContext context, protected ConditionOutcome getResourceOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
List<String> found = new ArrayList<String>();
for (String location : this.resourceLocations) { for (String location : this.resourceLocations) {
Resource resource = context.getResourceLoader().getResource(location); Resource resource = context.getResourceLoader().getResource(location);
if (resource != null && resource.exists()) { if (resource != null && resource.exists()) {
return ConditionOutcome found.add(location);
.match("Found " + this.name + " config in " + resource);
} }
} }
return ConditionOutcome if (found.isEmpty()) {
.noMatch("No specific " + this.name + " configuration found"); ConditionMessage message = startConditionMessage()
.didNotFind("resource", "resources")
.items(Style.QUOTE, Arrays.asList(this.resourceLocations));
return ConditionOutcome.noMatch(message);
}
ConditionMessage message = startConditionMessage().found("resource", "resources")
.items(Style.QUOTE, found);
return ConditionOutcome.match(message);
}
protected final Builder startConditionMessage() {
return ConditionMessage.forCondition("ResourceCondition", "(" + this.name + ")");
} }
} }
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -43,8 +43,8 @@ public abstract class HazelcastConfigResourceCondition extends ResourceCondition ...@@ -43,8 +43,8 @@ public abstract class HazelcastConfigResourceCondition extends ResourceCondition
protected ConditionOutcome getResourceOutcome(ConditionContext context, protected ConditionOutcome getResourceOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
if (System.getProperty(CONFIG_SYSTEM_PROPERTY) != null) { if (System.getProperty(CONFIG_SYSTEM_PROPERTY) != null) {
return ConditionOutcome return ConditionOutcome.match(startConditionMessage()
.match("System property '" + CONFIG_SYSTEM_PROPERTY + "' is set."); .because("System property '" + CONFIG_SYSTEM_PROPERTY + "' is set."));
} }
return super.getResourceOutcome(context, metadata); return super.getResourceOutcome(context, metadata);
} }
......
...@@ -20,6 +20,7 @@ import java.io.IOException; ...@@ -20,6 +20,7 @@ import java.io.IOException;
import java.util.Properties; import java.util.Properties;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;
...@@ -104,9 +105,13 @@ public class ProjectInfoAutoConfiguration { ...@@ -104,9 +105,13 @@ public class ProjectInfoAutoConfiguration {
location = "classpath:git.properties"; location = "classpath:git.properties";
} }
} }
boolean match = loader.getResource(location).exists(); ConditionMessage.Builder message = ConditionMessage
return new ConditionOutcome(match, .forCondition("GitResource");
"Git info " + (match ? "found" : "not found") + " at " + location); if (loader.getResource(location).exists()) {
return ConditionOutcome.match(message.found("git info at").items(location));
}
return ConditionOutcome
.noMatch(message.didNotFind("git info at").items(location));
} }
} }
......
...@@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.BeanDefinition; ...@@ -31,6 +31,7 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition; import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
...@@ -156,10 +157,14 @@ public class DataSourceAutoConfiguration { ...@@ -156,10 +157,14 @@ public class DataSourceAutoConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("PooledDataSource");
if (getDataSourceClassLoader(context) != null) { if (getDataSourceClassLoader(context) != null) {
return ConditionOutcome.match("supported DataSource class found"); return ConditionOutcome
.match(message.foundExactly("supported DataSource"));
} }
return ConditionOutcome.noMatch("missing supported DataSource"); return ConditionOutcome
.noMatch(message.didNotFind("supported DataSource").atAll());
} }
/** /**
...@@ -187,15 +192,19 @@ public class DataSourceAutoConfiguration { ...@@ -187,15 +192,19 @@ public class DataSourceAutoConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("EmbeddedDataSource");
if (anyMatches(context, metadata, this.pooledCondition)) { if (anyMatches(context, metadata, this.pooledCondition)) {
return ConditionOutcome.noMatch("supported DataSource class found"); return ConditionOutcome
.noMatch(message.foundExactly("supported pooled data source"));
} }
EmbeddedDatabaseType type = EmbeddedDatabaseConnection EmbeddedDatabaseType type = EmbeddedDatabaseConnection
.get(context.getClassLoader()).getType(); .get(context.getClassLoader()).getType();
if (type == null) { if (type == null) {
return ConditionOutcome.noMatch("no embedded database detected"); return ConditionOutcome
.noMatch(message.didNotFind("embedded database").atAll());
} }
return ConditionOutcome.match("embedded database " + type + " detected"); return ConditionOutcome.match(message.found("embedded database").items(type));
} }
} }
...@@ -214,16 +223,20 @@ public class DataSourceAutoConfiguration { ...@@ -214,16 +223,20 @@ public class DataSourceAutoConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("EmbeddedDataAvailble");
if (hasBean(context, DataSource.class) if (hasBean(context, DataSource.class)
|| hasBean(context, XADataSource.class)) { || hasBean(context, XADataSource.class)) {
return ConditionOutcome return ConditionOutcome
.match("existing bean configured database detected"); .match(message.foundExactly("existing database bean"));
} }
if (anyMatches(context, metadata, this.pooledCondition, if (anyMatches(context, metadata, this.pooledCondition,
this.embeddedCondition)) { this.embeddedCondition)) {
return ConditionOutcome.match("existing auto database detected"); return ConditionOutcome
.match(message.foundExactly("existing auto-configured database"));
} }
return ConditionOutcome.noMatch("no existing bean configured database"); return ConditionOutcome
.noMatch(message.didNotFind("any existing data source bean").atAll());
} }
private boolean hasBean(ConditionContext context, Class<?> type) { private boolean hasBean(ConditionContext context, Class<?> type) {
......
...@@ -116,8 +116,9 @@ public class ConditionEvaluationReportMessage { ...@@ -116,8 +116,9 @@ public class ConditionEvaluationReportMessage {
.append(String.format("%n")); .append(String.format("%n"));
for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) { for (ConditionAndOutcome conditionAndOutcome : conditionAndOutcomes) {
message.append(" - "); message.append(" - ");
if (StringUtils.hasLength(conditionAndOutcome.getOutcome().getMessage())) { String outcomeMessage = conditionAndOutcome.getOutcome().getMessage();
message.append(conditionAndOutcome.getOutcome().getMessage()); if (StringUtils.hasLength(outcomeMessage)) {
message.append(outcomeMessage);
} }
else { else {
message.append(conditionAndOutcome.getOutcome().isMatch() ? "matched" message.append(conditionAndOutcome.getOutcome().isMatch() ? "matched"
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.orm.jpa; package org.springframework.boot.autoconfigure.orm.jpa;
import java.util.Arrays;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
...@@ -28,6 +29,8 @@ import org.apache.commons.logging.LogFactory; ...@@ -28,6 +29,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
...@@ -199,13 +202,18 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration { ...@@ -199,13 +202,18 @@ public class HibernateJpaAutoConfiguration extends JpaBaseConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("HibernateEntityManager");
for (String className : CLASS_NAMES) { for (String className : CLASS_NAMES) {
if (ClassUtils.isPresent(className, context.getClassLoader())) { if (ClassUtils.isPresent(className, context.getClassLoader())) {
return ConditionOutcome.match("found HibernateEntityManager class"); return ConditionOutcome
.match(message.found("class").items(Style.QUOTE, className));
} }
} }
return ConditionOutcome.noMatch("did not find HibernateEntityManager class"); return ConditionOutcome.noMatch(message.didNotFind("class", "classes")
.items(Style.QUOTE, Arrays.asList(CLASS_NAMES)));
} }
} }
} }
/*
* Copyright 2012-2016 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.security.oauth2.client;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* Condition that checks for {@link EnableOAuth2Sso} on a
* {@link WebSecurityConfigurerAdapter}.
*
* @author Dave Syer
*/
class EnableOAuth2SsoCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String[] enablers = context.getBeanFactory()
.getBeanNamesForAnnotation(EnableOAuth2Sso.class);
ConditionMessage.Builder message = ConditionMessage
.forCondition("@EnableOAuth2Sso Condition");
for (String name : enablers) {
if (context.getBeanFactory().isTypeMatch(name,
WebSecurityConfigurerAdapter.class)) {
return ConditionOutcome.match(message
.found("@EnableOAuth2Sso annotation on WebSecurityConfigurerAdapter")
.items(name));
}
}
return ConditionOutcome.noMatch(message.didNotFind(
"@EnableOAuth2Sso annotation " + "on any WebSecurityConfigurerAdapter")
.atAll());
}
}
...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.security.oauth2.client; ...@@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.security.oauth2.client;
import javax.annotation.Resource; import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
...@@ -161,8 +162,14 @@ public class OAuth2RestOperationsConfiguration { ...@@ -161,8 +162,14 @@ public class OAuth2RestOperationsConfiguration {
PropertyResolver resolver = new RelaxedPropertyResolver( PropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "security.oauth2.client."); context.getEnvironment(), "security.oauth2.client.");
String clientId = resolver.getProperty("client-id"); String clientId = resolver.getProperty("client-id");
return new ConditionOutcome(StringUtils.hasLength(clientId), ConditionMessage.Builder message = ConditionMessage
"Non empty security.oauth2.client.client-id"); .forCondition("OAuth Client ID");
if (StringUtils.hasLength(clientId)) {
return ConditionOutcome.match(message
.foundExactly("security.oauth2.client.client-id property"));
}
return ConditionOutcome.match(message
.didNotFind("security.oauth2.client.client-id property").atAll());
} }
} }
......
...@@ -24,16 +24,11 @@ import org.aopalliance.intercept.MethodInvocation; ...@@ -24,16 +24,11 @@ import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.framework.ProxyFactory;
import org.springframework.beans.BeansException; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoCustomConfiguration.WebSecurityEnhancerCondition;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware; import org.springframework.context.annotation.ImportAware;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
...@@ -49,7 +44,7 @@ import org.springframework.util.ReflectionUtils; ...@@ -49,7 +44,7 @@ import org.springframework.util.ReflectionUtils;
* @author Dave Syer * @author Dave Syer
*/ */
@Configuration @Configuration
@Conditional(WebSecurityEnhancerCondition.class) @Conditional(EnableOAuth2SsoCondition.class)
public class OAuth2SsoCustomConfiguration public class OAuth2SsoCustomConfiguration
implements ImportAware, BeanPostProcessor, ApplicationContextAware { implements ImportAware, BeanPostProcessor, ApplicationContextAware {
...@@ -111,24 +106,4 @@ public class OAuth2SsoCustomConfiguration ...@@ -111,24 +106,4 @@ public class OAuth2SsoCustomConfiguration
} }
protected static class WebSecurityEnhancerCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String[] enablers = context.getBeanFactory()
.getBeanNamesForAnnotation(EnableOAuth2Sso.class);
for (String name : enablers) {
if (context.getBeanFactory().isTypeMatch(name,
WebSecurityConfigurerAdapter.class)) {
return ConditionOutcome.match(
"found @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
}
}
return ConditionOutcome.noMatch(
"found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
}
}
} }
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
package org.springframework.boot.autoconfigure.security.oauth2.client; package org.springframework.boot.autoconfigure.security.oauth2.client;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoDefaultConfiguration.NeedsWebSecurityCondition; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2SsoDefaultConfiguration.NeedsWebSecurityCondition;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
...@@ -75,22 +74,12 @@ public class OAuth2SsoDefaultConfiguration extends WebSecurityConfigurerAdapter ...@@ -75,22 +74,12 @@ public class OAuth2SsoDefaultConfiguration extends WebSecurityConfigurerAdapter
return SecurityProperties.ACCESS_OVERRIDE_ORDER; return SecurityProperties.ACCESS_OVERRIDE_ORDER;
} }
protected static class NeedsWebSecurityCondition extends SpringBootCondition { protected static class NeedsWebSecurityCondition extends EnableOAuth2SsoCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
String[] enablers = context.getBeanFactory() return ConditionOutcome.inverse(super.getMatchOutcome(context, metadata));
.getBeanNamesForAnnotation(EnableOAuth2Sso.class);
for (String name : enablers) {
if (context.getBeanFactory().isTypeMatch(name,
WebSecurityConfigurerAdapter.class)) {
return ConditionOutcome.noMatch(
"found @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
}
}
return ConditionOutcome
.match("found no @EnableOAuth2Sso on a WebSecurityConfigurerAdapter");
} }
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
...@@ -112,28 +113,32 @@ public class OAuth2ResourceServerConfiguration { ...@@ -112,28 +113,32 @@ public class OAuth2ResourceServerConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("OAuth ResourceServer Condition");
Environment environment = context.getEnvironment(); Environment environment = context.getEnvironment();
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"security.oauth2.resource."); "security.oauth2.resource.");
if (hasOAuthClientId(environment)) { if (hasOAuthClientId(environment)) {
return ConditionOutcome.match("found client id"); return ConditionOutcome.match(message.foundExactly("client-id property"));
} }
if (!resolver.getSubProperties("jwt").isEmpty()) { if (!resolver.getSubProperties("jwt").isEmpty()) {
return ConditionOutcome.match("found JWT resource configuration"); return ConditionOutcome
.match(message.foundExactly("JWT resource configuration"));
} }
if (StringUtils.hasText(resolver.getProperty("user-info-uri"))) { if (StringUtils.hasText(resolver.getProperty("user-info-uri"))) {
return ConditionOutcome return ConditionOutcome
.match("found UserInfo " + "URI resource configuration"); .match(message.foundExactly("user-info-url property"));
} }
if (ClassUtils.isPresent(AUTHORIZATION_ANNOTATION, null)) { if (ClassUtils.isPresent(AUTHORIZATION_ANNOTATION, null)) {
if (AuthorizationServerEndpointsConfigurationBeanCondition if (AuthorizationServerEndpointsConfigurationBeanCondition
.matches(context)) { .matches(context)) {
return ConditionOutcome.match( return ConditionOutcome
"found authorization " + "server endpoints configuration"); .match(message.found("class").items(AUTHORIZATION_ANNOTATION));
} }
} }
return ConditionOutcome.noMatch("found neither client id nor " return ConditionOutcome.noMatch(
+ "JWT resource nor authorization server"); message.didNotFind("client id, JWT resource or authorization server")
.atAll());
} }
private boolean hasOAuthClientId(Environment environment) { private boolean hasOAuthClientId(Environment environment) {
......
...@@ -25,6 +25,7 @@ import org.apache.commons.logging.Log; ...@@ -25,6 +25,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
...@@ -292,6 +293,8 @@ public class ResourceServerTokenServicesConfiguration { ...@@ -292,6 +293,8 @@ public class ResourceServerTokenServicesConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("OAuth TokenInfo Condition");
Environment environment = context.getEnvironment(); Environment environment = context.getEnvironment();
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment, RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(environment,
"security.oauth2.resource."); "security.oauth2.resource.");
...@@ -305,13 +308,14 @@ public class ResourceServerTokenServicesConfiguration { ...@@ -305,13 +308,14 @@ public class ResourceServerTokenServicesConfiguration {
String tokenInfoUri = resolver.getProperty("token-info-uri"); String tokenInfoUri = resolver.getProperty("token-info-uri");
String userInfoUri = resolver.getProperty("user-info-uri"); String userInfoUri = resolver.getProperty("user-info-uri");
if (!StringUtils.hasLength(userInfoUri)) { if (!StringUtils.hasLength(userInfoUri)) {
return ConditionOutcome.match("No user info provided"); return ConditionOutcome
.match(message.didNotFind("user-info-uri property").atAll());
} }
if (StringUtils.hasLength(tokenInfoUri) && preferTokenInfo) { if (StringUtils.hasLength(tokenInfoUri) && preferTokenInfo) {
return ConditionOutcome.match( return ConditionOutcome
"Token info endpoint " + "is preferred and user info provided"); .match(message.foundExactly("preferred token-info-uri property"));
} }
return ConditionOutcome.noMatch("Token info endpoint is not provided"); return ConditionOutcome.noMatch(message.didNotFind("token info").atAll());
} }
} }
...@@ -321,14 +325,18 @@ public class ResourceServerTokenServicesConfiguration { ...@@ -321,14 +325,18 @@ public class ResourceServerTokenServicesConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("OAuth JWT Condition");
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "security.oauth2.resource.jwt."); context.getEnvironment(), "security.oauth2.resource.jwt.");
String keyValue = resolver.getProperty("key-value"); String keyValue = resolver.getProperty("key-value");
String keyUri = resolver.getProperty("key-uri"); String keyUri = resolver.getProperty("key-uri");
if (StringUtils.hasText(keyValue) || StringUtils.hasText(keyUri)) { if (StringUtils.hasText(keyValue) || StringUtils.hasText(keyUri)) {
return ConditionOutcome.match("public key is provided"); return ConditionOutcome
.match(message.foundExactly("provided public key"));
} }
return ConditionOutcome.noMatch("public key is not provided"); return ConditionOutcome
.noMatch(message.didNotFind("provided public key").atAll());
} }
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.session; package org.springframework.boot.autoconfigure.session;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
...@@ -38,22 +39,27 @@ class SessionCondition extends SpringBootCondition { ...@@ -38,22 +39,27 @@ class SessionCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("Session Condition");
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.session."); context.getEnvironment(), "spring.session.");
StoreType sessionStoreType = SessionStoreMappings StoreType sessionStoreType = SessionStoreMappings
.getType(((AnnotationMetadata) metadata).getClassName()); .getType(((AnnotationMetadata) metadata).getClassName());
if (!resolver.containsProperty("store-type")) { if (!resolver.containsProperty("store-type")) {
if (sessionStoreType == StoreType.REDIS && redisPresent) { if (sessionStoreType == StoreType.REDIS && redisPresent) {
return ConditionOutcome return ConditionOutcome.match(
.match("Session store type default to redis (deprecated)"); message.foundExactly("default store type of redis (deprecated)"));
} }
return ConditionOutcome.noMatch("Session store type not set"); return ConditionOutcome.noMatch(
message.didNotFind("spring.session.store-type property").atAll());
} }
String value = resolver.getProperty("store-type").replace("-", "_").toUpperCase(); String value = resolver.getProperty("store-type").replace("-", "_").toUpperCase();
if (value.equals(sessionStoreType.name())) { if (value.equals(sessionStoreType.name())) {
return ConditionOutcome.match("Session store type " + sessionStoreType); return ConditionOutcome.match(message
.found("spring.session.store-type property").items(sessionStoreType));
} }
return ConditionOutcome.noMatch("Session store type " + value); return ConditionOutcome
.noMatch(message.found("spring.session.store-type property").items(value));
} }
} }
...@@ -27,6 +27,8 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; ...@@ -27,6 +27,8 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
...@@ -153,23 +155,28 @@ public class DispatcherServletAutoConfiguration { ...@@ -153,23 +155,28 @@ public class DispatcherServletAutoConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("Default DispatcherServlet");
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
List<String> dispatchServletBeans = Arrays.asList(beanFactory List<String> dispatchServletBeans = Arrays.asList(beanFactory
.getBeanNamesForType(DispatcherServlet.class, false, false)); .getBeanNamesForType(DispatcherServlet.class, false, false));
if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch("found DispatcherServlet named " return ConditionOutcome.noMatch(message.found("dispatcher servlet bean")
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); .items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
} }
if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch("found non-DispatcherServlet named " return ConditionOutcome
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); .noMatch(message.found("non dispatcher servlet bean")
.items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
} }
if (dispatchServletBeans.isEmpty()) { if (dispatchServletBeans.isEmpty()) {
return ConditionOutcome.match("no DispatcherServlet found"); return ConditionOutcome
.match(message.didNotFind("dispatcher servlet beans").atAll());
} }
return ConditionOutcome return ConditionOutcome.match(message
.match("one or more DispatcherServlets found and none is named " .found("dipatcher servlet bean", "dispatcher servlet beans")
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); .items(Style.QUOTE, dispatchServletBeans)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
} }
} }
...@@ -197,39 +204,48 @@ public class DispatcherServletAutoConfiguration { ...@@ -197,39 +204,48 @@ public class DispatcherServletAutoConfiguration {
.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); .containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (containsDispatcherBean if (containsDispatcherBean
&& !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) { && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch("found non-DispatcherServlet named " return ConditionOutcome
+ DEFAULT_DISPATCHER_SERVLET_BEAN_NAME); .noMatch(startMessage().found("non dispatcher servlet")
.items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
} }
return ConditionOutcome.match(); return ConditionOutcome.match();
} }
private ConditionOutcome checkServletRegistration( private ConditionOutcome checkServletRegistration(
ConfigurableListableBeanFactory beanFactory) { ConfigurableListableBeanFactory beanFactory) {
ConditionMessage.Builder message = startMessage();
List<String> registrations = Arrays.asList(beanFactory List<String> registrations = Arrays.asList(beanFactory
.getBeanNamesForType(ServletRegistrationBean.class, false, false)); .getBeanNamesForType(ServletRegistrationBean.class, false, false));
boolean containsDispatcherRegistrationBean = beanFactory boolean containsDispatcherRegistrationBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); .containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
if (registrations.isEmpty()) { if (registrations.isEmpty()) {
if (containsDispatcherRegistrationBean) { if (containsDispatcherRegistrationBean) {
return ConditionOutcome.noMatch("found no ServletRegistrationBean " return ConditionOutcome
+ "but a non-ServletRegistrationBean named " .noMatch(message.found("non servlet registration bean").items(
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
} }
return ConditionOutcome.match("no ServletRegistrationBean found"); return ConditionOutcome
.match(message.didNotFind("servlet registration bean").atAll());
} }
if (registrations if (registrations
.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) { .contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
return ConditionOutcome.noMatch("found ServletRegistrationBean named " return ConditionOutcome.noMatch(message.found("servlet registration bean")
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); .items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
} }
if (containsDispatcherRegistrationBean) { if (containsDispatcherRegistrationBean) {
return ConditionOutcome.noMatch("found non-ServletRegistrationBean named " return ConditionOutcome
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); .noMatch(message.found("non servlet registration bean").items(
DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
} }
return ConditionOutcome return ConditionOutcome.match(message.found("servlet registration beans")
.match("one or more ServletRegistrationBeans is found and none is named " .items(Style.QUOTE, registrations).append("and none is named "
+ DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME); + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
} }
private ConditionMessage.Builder startMessage() {
return ConditionMessage.forCondition("DispatcherServlet Registration");
}
} }
} }
...@@ -32,6 +32,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor; ...@@ -32,6 +32,7 @@ import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
...@@ -166,6 +167,8 @@ public class ErrorMvcAutoConfiguration { ...@@ -166,6 +167,8 @@ public class ErrorMvcAutoConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("ErrorTemplate Misssing");
TemplateAvailabilityProviders providers = new TemplateAvailabilityProviders( TemplateAvailabilityProviders providers = new TemplateAvailabilityProviders(
context.getClassLoader()); context.getClassLoader());
TemplateAvailabilityProvider provider = providers.getProvider("error", TemplateAvailabilityProvider provider = providers.getProvider("error",
...@@ -173,9 +176,10 @@ public class ErrorMvcAutoConfiguration { ...@@ -173,9 +176,10 @@ public class ErrorMvcAutoConfiguration {
context.getResourceLoader()); context.getResourceLoader());
if (provider != null) { if (provider != null) {
return ConditionOutcome return ConditionOutcome
.noMatch("Template from " + provider + " found for error view"); .noMatch(message.foundExactly("template from " + provider));
} }
return ConditionOutcome.match("No error template view detected"); return ConditionOutcome
.match(message.didNotFind("error template view").atAll());
} }
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.web; package org.springframework.boot.autoconfigure.web;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.PropertySourcesPropertyValues; import org.springframework.boot.bind.PropertySourcesPropertyValues;
...@@ -31,6 +32,7 @@ import org.springframework.util.ClassUtils; ...@@ -31,6 +32,7 @@ import org.springframework.util.ClassUtils;
* enabled. * enabled.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @see ConditionalOnEnabledResourceChain
*/ */
class OnEnabledResourceChainCondition extends SpringBootCondition { class OnEnabledResourceChainCondition extends SpringBootCondition {
...@@ -45,15 +47,21 @@ class OnEnabledResourceChainCondition extends SpringBootCondition { ...@@ -45,15 +47,21 @@ class OnEnabledResourceChainCondition extends SpringBootCondition {
RelaxedDataBinder binder = new RelaxedDataBinder(properties, "spring.resources"); RelaxedDataBinder binder = new RelaxedDataBinder(properties, "spring.resources");
binder.bind(new PropertySourcesPropertyValues(environment.getPropertySources())); binder.bind(new PropertySourcesPropertyValues(environment.getPropertySources()));
Boolean match = properties.getChain().getEnabled(); Boolean match = properties.getChain().getEnabled();
ConditionMessage.Builder message = ConditionMessage
.forCondition(ConditionalOnEnabledResourceChain.class);
if (match == null) { if (match == null) {
boolean webJarsLocatorPresent = ClassUtils.isPresent(WEBJAR_ASSERT_LOCATOR, if (ClassUtils.isPresent(WEBJAR_ASSERT_LOCATOR,
getClass().getClassLoader()); getClass().getClassLoader())) {
return new ConditionOutcome(webJarsLocatorPresent, return ConditionOutcome
"Webjars locator (" + WEBJAR_ASSERT_LOCATOR + ") is " .match(message.found("class").items(WEBJAR_ASSERT_LOCATOR));
+ (webJarsLocatorPresent ? "present" : "absent")); }
return ConditionOutcome
.noMatch(message.didNotFind("class").items(WEBJAR_ASSERT_LOCATOR));
} }
return new ConditionOutcome(match, if (match) {
"Resource chain is " + (match ? "enabled" : "disabled")); return ConditionOutcome.match(message.because("enabled"));
}
return ConditionOutcome.noMatch(message.because("disabled"));
} }
} }
...@@ -216,9 +216,11 @@ public class ConditionEvaluationReportTests { ...@@ -216,9 +216,11 @@ public class ConditionEvaluationReportTests {
for (ConditionAndOutcome outcome : outcomes) { for (ConditionAndOutcome outcome : outcomes) {
messages.add(outcome.getOutcome().getMessage()); messages.add(outcome.getOutcome().getMessage());
} }
assertThat(messages).areAtLeastOne(Matched.by( assertThat(messages).areAtLeastOne(
containsString("@ConditionalOnClass classes found: javax.servlet.Servlet," Matched.by(containsString("@ConditionalOnClass found required classes "
+ "org.springframework.web.multipart.support.StandardServletMultipartResolver"))); + "'javax.servlet.Servlet', 'org.springframework.web.multipart."
+ "support.StandardServletMultipartResolver', "
+ "'javax.servlet.MultipartConfigElement'")));
context.close(); context.close();
} }
......
/*
* Copyright 2012-2016 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 java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ConditionMessage}.
*
* @author Phillip Webb
*/
public class ConditionMessageTests {
@Test
public void isEmptyWhenEmptyShouldReturnTrue() throws Exception {
ConditionMessage message = ConditionMessage.empty();
assertThat(message.isEmpty()).isTrue();
}
@Test
public void isEmptyWhenNotEmptyShouldReturnFalse() throws Exception {
ConditionMessage message = ConditionMessage.of("Test");
assertThat(message.isEmpty()).isFalse();
}
@Test
public void toStringWhenHasMessageShouldReturnMessage() throws Exception {
ConditionMessage message = ConditionMessage.empty();
assertThat(message.toString()).isEqualTo("");
}
@Test
public void toStringWhenEmptyShouldReturnEmptyString() throws Exception {
ConditionMessage message = ConditionMessage.of("Test");
assertThat(message.toString()).isEqualTo("Test");
}
@Test
public void appendWhenHasExistingMessageShouldAddSpace() throws Exception {
ConditionMessage message = ConditionMessage.of("a").append("b");
assertThat(message.toString()).isEqualTo("a b");
}
@Test
public void appendWhenAppendingNullShouldDoNothing() throws Exception {
ConditionMessage message = ConditionMessage.of("a").append(null);
assertThat(message.toString()).isEqualTo("a");
}
@Test
public void appendWhenNoMessageShouldNotAddSpace() throws Exception {
ConditionMessage message = ConditionMessage.empty().append("b");
assertThat(message.toString()).isEqualTo("b");
}
@Test
public void andConditionWhenUsingClassShouldIncludeCondition() throws Exception {
ConditionMessage message = ConditionMessage.empty().andCondition(Test.class)
.because("OK");
assertThat(message.toString()).isEqualTo("@Test OK");
}
@Test
public void andConditionWhenUsingStringShouldIncludeCondition() throws Exception {
ConditionMessage message = ConditionMessage.empty().andCondition("@Test")
.because("OK");
assertThat(message.toString()).isEqualTo("@Test OK");
}
@Test
public void andConditionWhenIncludingDetailsShouldIncludeCondition()
throws Exception {
ConditionMessage message = ConditionMessage.empty()
.andCondition(Test.class, "(a=b)").because("OK");
assertThat(message.toString()).isEqualTo("@Test (a=b) OK");
}
@Test
public void ofCollectionShouldCombine() throws Exception {
List<ConditionMessage> messages = new ArrayList<ConditionMessage>();
messages.add(ConditionMessage.of("a"));
messages.add(ConditionMessage.of("b"));
ConditionMessage message = ConditionMessage.of(messages);
assertThat(message.toString()).isEqualTo("a; b");
}
@Test
public void ofCollectionWhenNullShouldReturnEmpty() throws Exception {
ConditionMessage message = ConditionMessage.of((List<ConditionMessage>) null);
assertThat(message.isEmpty()).isTrue();
}
@Test
public void forConditionShouldIncludeCondition() throws Exception {
ConditionMessage message = ConditionMessage.forCondition("@Test").because("OK");
assertThat(message.toString()).isEqualTo("@Test OK");
}
@Test
public void forConditionWhenClassShouldIncludeCondition() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class, "(a=b)")
.because("OK");
assertThat(message.toString()).isEqualTo("@Test (a=b) OK");
}
@Test
public void foundExactlyShouldConstructMessage() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.foundExactly("abc");
assertThat(message.toString()).isEqualTo("@Test found abc");
}
@Test
public void foundWhenSingleElementShouldUsingSingular() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.found("bean", "beans").items("a");
assertThat(message.toString()).isEqualTo("@Test found bean a");
}
@Test
public void foundNoneAtAllShouldConstructMessage() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.found("no beans").atAll();
assertThat(message.toString()).isEqualTo("@Test found no beans");
}
@Test
public void foundWhenMultipleElementsShouldUsePlural() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.found("bean", "beans").items("a", "b", "c");
assertThat(message.toString()).isEqualTo("@Test found beans a, b, c");
}
@Test
public void foundWhenQuoteStyleShouldQuote() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.found("bean", "beans").items(Style.QUOTE, "a", "b", "c");
assertThat(message.toString()).isEqualTo("@Test found beans 'a', 'b', 'c'");
}
@Test
public void didNotFindWhenSingleElementShouldUsingSingular() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.didNotFind("class", "classes").items("a");
assertThat(message.toString()).isEqualTo("@Test did not find class a");
}
@Test
public void didNotFindWhenMultipleElementsShouldUsePlural() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.didNotFind("class", "classes").items("a", "b", "c");
assertThat(message.toString()).isEqualTo("@Test did not find classes a, b, c");
}
@Test
public void resultedInShouldConstructMessage() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.resultedIn("Green");
assertThat(message.toString()).isEqualTo("@Test resulted in Green");
}
@Test
public void notAvailableShouldConstructMessage() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.notAvailable("JMX");
assertThat(message.toString()).isEqualTo("@Test JMX is not available");
}
@Test
public void availableShouldConstructMessage() throws Exception {
ConditionMessage message = ConditionMessage.forCondition(Test.class)
.available("JMX");
assertThat(message.toString()).isEqualTo("@Test JMX is available");
}
}
...@@ -70,37 +70,45 @@ public class ConditionalOnClassTests { ...@@ -70,37 +70,45 @@ public class ConditionalOnClassTests {
@Configuration @Configuration
@ConditionalOnClass(ConditionalOnClassTests.class) @ConditionalOnClass(ConditionalOnClassTests.class)
protected static class BasicConfiguration { protected static class BasicConfiguration {
@Bean @Bean
public String bar() { public String bar() {
return "bar"; return "bar";
} }
} }
@Configuration @Configuration
@ConditionalOnClass(name = "FOO") @ConditionalOnClass(name = "FOO")
protected static class MissingConfiguration { protected static class MissingConfiguration {
@Bean @Bean
public String bar() { public String bar() {
return "bar"; return "bar";
} }
} }
@Configuration @Configuration
protected static class FooConfiguration { protected static class FooConfiguration {
@Bean @Bean
public String foo() { public String foo() {
return "foo"; return "foo";
} }
} }
@Configuration @Configuration
@ImportResource("org/springframework/boot/autoconfigure/condition/foo.xml") @ImportResource("org/springframework/boot/autoconfigure/condition/foo.xml")
protected static class XmlConfiguration { protected static class XmlConfiguration {
} }
@Configuration @Configuration
@Import(BasicConfiguration.class) @Import(BasicConfiguration.class)
@ImportResource("org/springframework/boot/autoconfigure/condition/foo.xml") @ImportResource("org/springframework/boot/autoconfigure/condition/foo.xml")
protected static class CombinedXmlConfiguration { protected static class CombinedXmlConfiguration {
} }
} }
...@@ -81,7 +81,7 @@ public class ConditionalOnJavaTests { ...@@ -81,7 +81,7 @@ public class ConditionalOnJavaTests {
ConditionOutcome outcome = this.condition.getMatchOutcome(Range.EQUAL_OR_NEWER, ConditionOutcome outcome = this.condition.getMatchOutcome(Range.EQUAL_OR_NEWER,
JavaVersion.SEVEN, JavaVersion.SIX); JavaVersion.SEVEN, JavaVersion.SIX);
assertThat(outcome.getMessage()) assertThat(outcome.getMessage())
.isEqualTo("Required JVM version " + "1.6 or newer found 1.7"); .isEqualTo("@ConditionalOnJava (1.6 or newer) found 1.7");
} }
@Test @Test
...@@ -89,7 +89,7 @@ public class ConditionalOnJavaTests { ...@@ -89,7 +89,7 @@ public class ConditionalOnJavaTests {
ConditionOutcome outcome = this.condition.getMatchOutcome(Range.OLDER_THAN, ConditionOutcome outcome = this.condition.getMatchOutcome(Range.OLDER_THAN,
JavaVersion.SEVEN, JavaVersion.SIX); JavaVersion.SEVEN, JavaVersion.SIX);
assertThat(outcome.getMessage()) assertThat(outcome.getMessage())
.isEqualTo("Required JVM version " + "older than 1.6 found 1.7"); .isEqualTo("@ConditionalOnJava (older than 1.6) found 1.7");
} }
@Test @Test
......
...@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; ...@@ -27,6 +27,7 @@ import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
...@@ -126,16 +127,18 @@ public class DevToolsDataSourceAutoConfiguration { ...@@ -126,16 +127,18 @@ public class DevToolsDataSourceAutoConfiguration {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("DevTools DataSource Condition");
String[] dataSourceBeanNames = context.getBeanFactory() String[] dataSourceBeanNames = context.getBeanFactory()
.getBeanNamesForType(DataSource.class); .getBeanNamesForType(DataSource.class);
if (dataSourceBeanNames.length != 1) { if (dataSourceBeanNames.length != 1) {
return ConditionOutcome return ConditionOutcome
.noMatch("A single DataSource bean was not found in the context"); .noMatch(message.didNotFind("a single DataSource bean").atAll());
} }
if (context.getBeanFactory() if (context.getBeanFactory()
.getBeanNamesForType(DataSourceProperties.class).length != 1) { .getBeanNamesForType(DataSourceProperties.class).length != 1) {
return ConditionOutcome.noMatch( return ConditionOutcome.noMatch(
"A single DataSourceProperties bean was not found in the context"); message.didNotFind("a single DataSourceProperties bean").atAll());
} }
BeanDefinition dataSourceDefinition = context.getRegistry() BeanDefinition dataSourceDefinition = context.getRegistry()
.getBeanDefinition(dataSourceBeanNames[0]); .getBeanDefinition(dataSourceBeanNames[0]);
...@@ -146,9 +149,11 @@ public class DevToolsDataSourceAutoConfiguration { ...@@ -146,9 +149,11 @@ public class DevToolsDataSourceAutoConfiguration {
.getFactoryMethodMetadata().getDeclaringClassName() .getFactoryMethodMetadata().getDeclaringClassName()
.startsWith(DataSourceAutoConfiguration.class.getPackage() .startsWith(DataSourceAutoConfiguration.class.getPackage()
.getName() + ".DataSourceConfiguration$")) { .getName() + ".DataSourceConfiguration$")) {
return ConditionOutcome.match("Found auto-configured DataSource"); return ConditionOutcome
.match(message.foundExactly("auto-configured DataSource"));
} }
return ConditionOutcome.noMatch("DataSource was not auto-configured"); return ConditionOutcome
.noMatch(message.didNotFind("an auto-configured DataSource").atAll());
} }
} }
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -18,6 +18,7 @@ package org.springframework.boot.devtools.remote.client; ...@@ -18,6 +18,7 @@ package org.springframework.boot.devtools.remote.client;
import javax.net.ServerSocketFactory; import javax.net.ServerSocketFactory;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
...@@ -35,6 +36,8 @@ class LocalDebugPortAvailableCondition extends SpringBootCondition { ...@@ -35,6 +36,8 @@ class LocalDebugPortAvailableCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("Local Debug Port Condition");
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver( RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.devtools.remote.debug."); context.getEnvironment(), "spring.devtools.remote.debug.");
Integer port = resolver.getProperty("local-port", Integer.class); Integer port = resolver.getProperty("local-port", Integer.class);
...@@ -42,9 +45,9 @@ class LocalDebugPortAvailableCondition extends SpringBootCondition { ...@@ -42,9 +45,9 @@ class LocalDebugPortAvailableCondition extends SpringBootCondition {
port = RemoteDevToolsProperties.Debug.DEFAULT_LOCAL_PORT; port = RemoteDevToolsProperties.Debug.DEFAULT_LOCAL_PORT;
} }
if (isPortAvailable(port)) { if (isPortAvailable(port)) {
return ConditionOutcome.match("Local debug port available"); return ConditionOutcome.match(message.foundExactly("local debug port"));
} }
return ConditionOutcome.noMatch("Local debug port unavailable"); return ConditionOutcome.noMatch(message.didNotFind("local debug port").atAll());
} }
private boolean isPortAvailable(int port) { private boolean isPortAvailable(int port) {
......
/* /*
* Copyright 2012-2015 the original author or authors. * Copyright 2012-2016 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
package org.springframework.boot.devtools.restart; package org.springframework.boot.devtools.restart;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome; import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition; import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
...@@ -33,14 +34,16 @@ class OnInitializedRestarterCondition extends SpringBootCondition { ...@@ -33,14 +34,16 @@ class OnInitializedRestarterCondition extends SpringBootCondition {
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) { AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage
.forCondition("Initializer Restarter Condition");
Restarter restarter = getRestarter(); Restarter restarter = getRestarter();
if (restarter == null) { if (restarter == null) {
return ConditionOutcome.noMatch("Restarter unavailable"); return ConditionOutcome.noMatch(message.because("unavailable"));
} }
if (restarter.getInitialUrls() == null) { if (restarter.getInitialUrls() == null) {
return ConditionOutcome.noMatch("Restarter initialized without URLs"); return ConditionOutcome.noMatch(message.because("initialized without URLs"));
} }
return ConditionOutcome.match("Restarter available and initialized"); return ConditionOutcome.match(message.because("available and initialized"));
} }
private Restarter getRestarter() { private Restarter getRestarter() {
......
...@@ -47,7 +47,8 @@ public class LocalDebugPortAvailableConditionTests { ...@@ -47,7 +47,8 @@ public class LocalDebugPortAvailableConditionTests {
public void portAvailable() throws Exception { public void portAvailable() throws Exception {
ConditionOutcome outcome = getOutcome(); ConditionOutcome outcome = getOutcome();
assertThat(outcome.isMatch()).isTrue(); assertThat(outcome.isMatch()).isTrue();
assertThat(outcome.getMessage()).isEqualTo("Local debug port available"); assertThat(outcome.getMessage())
.isEqualTo("Local Debug Port Condition found local debug port");
} }
@Test @Test
...@@ -57,7 +58,8 @@ public class LocalDebugPortAvailableConditionTests { ...@@ -57,7 +58,8 @@ public class LocalDebugPortAvailableConditionTests {
ConditionOutcome outcome = getOutcome(); ConditionOutcome outcome = getOutcome();
serverSocket.close(); serverSocket.close();
assertThat(outcome.isMatch()).isFalse(); assertThat(outcome.isMatch()).isFalse();
assertThat(outcome.getMessage()).isEqualTo("Local debug port unavailable"); assertThat(outcome.getMessage())
.isEqualTo("Local Debug Port Condition did not find local debug port");
} }
private ConditionOutcome getOutcome() { private ConditionOutcome getOutcome() {
......
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