Commit 4d705dff authored by Phillip Webb's avatar Phillip Webb

Merge branch 'gh-13328'

parents 5765ed00 ff98ba0f
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 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.
...@@ -45,7 +45,8 @@ public interface AutoConfigurationImportFilter { ...@@ -45,7 +45,8 @@ public interface AutoConfigurationImportFilter {
/** /**
* Apply the filter to the given auto-configuration class candidates. * Apply the filter to the given auto-configuration class candidates.
* @param autoConfigurationClasses the auto-configuration classes being considered. * @param autoConfigurationClasses the auto-configuration classes being considered.
* Implementations should not change the values in this array. * This array may contain {@code null} elements. Implementations should not change the
* values in this array.
* @param autoConfigurationMetadata access to the meta-data generated by the * @param autoConfigurationMetadata access to the meta-data generated by the
* auto-configure annotation processor * auto-configure annotation processor
* @return a boolean array indicating which of the auto-configuration classes should * @return a boolean array indicating which of the auto-configuration classes should
......
...@@ -263,6 +263,7 @@ public class AutoConfigurationImportSelector ...@@ -263,6 +263,7 @@ public class AutoConfigurationImportSelector
for (int i = 0; i < match.length; i++) { for (int i = 0; i < match.length; i++) {
if (!match[i]) { if (!match[i]) {
skip[i] = true; skip[i] = true;
candidates[i] = null;
skipped = true; skipped = true;
} }
} }
......
...@@ -29,6 +29,7 @@ import java.util.SortedMap; ...@@ -29,6 +29,7 @@ import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.ConditionContext;
...@@ -159,6 +160,20 @@ public final class ConditionEvaluationReport { ...@@ -159,6 +160,20 @@ public final class ConditionEvaluationReport {
return this.parent; return this.parent;
} }
/**
* Attempt to find the {@link ConditionEvaluationReport} for the specified bean
* factory.
* @param beanFactory the bean factory (may be {@code null})
* @return the {@link ConditionEvaluationReport} or {@code null}
*/
public static ConditionEvaluationReport find(BeanFactory beanFactory) {
if (beanFactory != null && beanFactory instanceof ConfigurableBeanFactory) {
return ConditionEvaluationReport
.get((ConfigurableListableBeanFactory) beanFactory);
}
return null;
}
/** /**
* Obtain a {@link ConditionEvaluationReport} for the specified bean factory. * Obtain a {@link ConditionEvaluationReport} for the specified bean factory.
* @param beanFactory the bean factory * @param beanFactory the bean factory
......
/*
* Copyright 2012-2018 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.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/**
* Abstract base class for a {@link SpringBootCondition} that also implements
* {@link AutoConfigurationImportFilter}.
*
* @author Phillip Webb
*/
abstract class FilteringSpringBootCondition extends SpringBootCondition
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
private BeanFactory beanFactory;
private ClassLoader beanClassLoader;
@Override
public boolean[] match(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport
.find(this.beanFactory);
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this,
outcomes[i]);
}
}
}
return match;
}
protected abstract ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata);
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
protected final BeanFactory getBeanFactory() {
return this.beanFactory;
}
protected final ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
protected List<String> filter(Collection<String> classNames,
ClassNameFilter classNameFilter, ClassLoader classLoader) {
if (CollectionUtils.isEmpty(classNames)) {
return Collections.emptyList();
}
List<String> matches = new ArrayList<>(classNames.size());
for (String candidate : classNames) {
if (classNameFilter.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}
protected enum ClassNameFilter {
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
public abstract boolean matches(String className, ClassLoader classLoader);
public static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
private static Class<?> forName(String className, ClassLoader classLoader)
throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
}
}
}
...@@ -34,6 +34,7 @@ import org.springframework.beans.factory.HierarchicalBeanFactory; ...@@ -34,6 +34,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.AutoConfigurationMetadata;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; 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;
...@@ -58,7 +59,8 @@ import org.springframework.util.StringUtils; ...@@ -58,7 +59,8 @@ import org.springframework.util.StringUtils;
* @author Andy Wilkinson * @author Andy Wilkinson
*/ */
@Order(Ordered.LOWEST_PRECEDENCE) @Order(Ordered.LOWEST_PRECEDENCE)
class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition { class OnBeanCondition extends FilteringSpringBootCondition
implements ConfigurationCondition {
/** /**
* Bean definition attribute name for factory beans to signal their product type (if * Bean definition attribute name for factory beans to signal their product type (if
...@@ -66,6 +68,40 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit ...@@ -66,6 +68,40 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
*/ */
public static final String FACTORY_BEAN_OBJECT_TYPE = BeanTypeRegistry.FACTORY_BEAN_OBJECT_TYPE; public static final String FACTORY_BEAN_OBJECT_TYPE = BeanTypeRegistry.FACTORY_BEAN_OBJECT_TYPE;
@Override
protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
for (int i = 0; i < outcomes.length; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
Set<String> onBeanTypes = autoConfigurationMetadata
.getSet(autoConfigurationClass, "ConditionalOnBean");
outcomes[i] = getOutcome(onBeanTypes, ConditionalOnBean.class);
if (outcomes[i] == null) {
Set<String> onSingleCandidateTypes = autoConfigurationMetadata.getSet(
autoConfigurationClass, "ConditionalOnSingleCandidate");
outcomes[i] = getOutcome(onSingleCandidateTypes,
ConditionalOnSingleCandidate.class);
}
}
}
return outcomes;
}
private ConditionOutcome getOutcome(Set<String> requiredBeanTypes,
Class<? extends Annotation> annotation) {
List<String> missing = filter(requiredBeanTypes, ClassNameFilter.MISSING,
getBeanClassLoader());
if (!missing.isEmpty()) {
ConditionMessage message = ConditionMessage.forCondition(annotation)
.didNotFind("required type", "required types")
.items(Style.QUOTE, missing);
return ConditionOutcome.noMatch(message);
}
return null;
}
@Override @Override
public ConfigurationPhase getConfigurationPhase() { public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN; return ConfigurationPhase.REGISTER_BEAN;
...@@ -337,7 +373,6 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit ...@@ -337,7 +373,6 @@ class OnBeanCondition extends SpringBootCondition implements ConfigurationCondit
.getParentBeanFactory()), beanName, considerHierarchy); .getParentBeanFactory()), beanName, considerHierarchy);
} }
return null; return null;
} }
private static class BeanSearchSpec { private static class BeanSearchSpec {
......
...@@ -18,17 +18,10 @@ package org.springframework.boot.autoconfigure.condition; ...@@ -18,17 +18,10 @@ package org.springframework.boot.autoconfigure.condition;
import java.security.AccessControlException; import java.security.AccessControlException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter; import org.springframework.boot.autoconfigure.AutoConfigurationImportFilter;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata; import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style; import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
...@@ -37,7 +30,6 @@ import org.springframework.context.annotation.ConditionContext; ...@@ -37,7 +30,6 @@ import org.springframework.context.annotation.ConditionContext;
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.util.ClassUtils;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
/** /**
...@@ -49,43 +41,10 @@ import org.springframework.util.MultiValueMap; ...@@ -49,43 +41,10 @@ import org.springframework.util.MultiValueMap;
* @see ConditionalOnMissingClass * @see ConditionalOnMissingClass
*/ */
@Order(Ordered.HIGHEST_PRECEDENCE) @Order(Ordered.HIGHEST_PRECEDENCE)
class OnClassCondition extends SpringBootCondition class OnClassCondition extends FilteringSpringBootCondition {
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
private BeanFactory beanFactory;
private ClassLoader beanClassLoader;
@Override @Override
public boolean[] match(String[] autoConfigurationClasses, protected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = getConditionEvaluationReport();
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses,
autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this,
outcomes[i]);
}
}
}
return match;
}
private ConditionEvaluationReport getConditionEvaluationReport() {
if (this.beanFactory != null
&& this.beanFactory instanceof ConfigurableBeanFactory) {
return ConditionEvaluationReport
.get((ConfigurableListableBeanFactory) this.beanFactory);
}
return null;
}
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) { AutoConfigurationMetadata autoConfigurationMetadata) {
// Split the work and perform half in a background thread. Using a single // Split the work and perform half in a background thread. Using a single
// additional thread seems to offer the best performance. More threads make // additional thread seems to offer the best performance. More threads make
...@@ -95,7 +54,7 @@ class OnClassCondition extends SpringBootCondition ...@@ -95,7 +54,7 @@ class OnClassCondition extends SpringBootCondition
autoConfigurationClasses, 0, split, autoConfigurationMetadata); autoConfigurationClasses, 0, split, autoConfigurationMetadata);
OutcomesResolver secondHalfResolver = new StandardOutcomesResolver( OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
autoConfigurationClasses, split, autoConfigurationClasses.length, autoConfigurationClasses, split, autoConfigurationClasses.length,
autoConfigurationMetadata, this.beanClassLoader); autoConfigurationMetadata, getBeanClassLoader());
ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes(); ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes(); ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length]; ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
...@@ -108,7 +67,7 @@ class OnClassCondition extends SpringBootCondition ...@@ -108,7 +67,7 @@ class OnClassCondition extends SpringBootCondition
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) { int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
OutcomesResolver outcomesResolver = new StandardOutcomesResolver( OutcomesResolver outcomesResolver = new StandardOutcomesResolver(
autoConfigurationClasses, start, end, autoConfigurationMetadata, autoConfigurationClasses, start, end, autoConfigurationMetadata,
this.beanClassLoader); getBeanClassLoader());
try { try {
return new ThreadedOutcomesResolver(outcomesResolver); return new ThreadedOutcomesResolver(outcomesResolver);
} }
...@@ -124,7 +83,8 @@ class OnClassCondition extends SpringBootCondition ...@@ -124,7 +83,8 @@ class OnClassCondition extends SpringBootCondition
ConditionMessage matchMessage = ConditionMessage.empty(); ConditionMessage matchMessage = ConditionMessage.empty();
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class); List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) { if (onClasses != null) {
List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader); List<String> missing = filter(onClasses, ClassNameFilter.MISSING,
classLoader);
if (!missing.isEmpty()) { if (!missing.isEmpty()) {
return ConditionOutcome return ConditionOutcome
.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class) .noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
...@@ -133,12 +93,12 @@ class OnClassCondition extends SpringBootCondition ...@@ -133,12 +93,12 @@ class OnClassCondition extends SpringBootCondition
} }
matchMessage = matchMessage.andCondition(ConditionalOnClass.class) matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes").items(Style.QUOTE, .found("required class", "required classes").items(Style.QUOTE,
getMatches(onClasses, MatchType.PRESENT, classLoader)); filter(onClasses, ClassNameFilter.PRESENT, classLoader));
} }
List<String> onMissingClasses = getCandidates(metadata, List<String> onMissingClasses = getCandidates(metadata,
ConditionalOnMissingClass.class); ConditionalOnMissingClass.class);
if (onMissingClasses != null) { if (onMissingClasses != null) {
List<String> present = getMatches(onMissingClasses, MatchType.PRESENT, List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT,
classLoader); classLoader);
if (!present.isEmpty()) { if (!present.isEmpty()) {
return ConditionOutcome.noMatch( return ConditionOutcome.noMatch(
...@@ -147,8 +107,9 @@ class OnClassCondition extends SpringBootCondition ...@@ -147,8 +107,9 @@ class OnClassCondition extends SpringBootCondition
.items(Style.QUOTE, present)); .items(Style.QUOTE, present));
} }
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class) matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
.didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE, .didNotFind("unwanted class", "unwanted classes")
getMatches(onMissingClasses, MatchType.MISSING, classLoader)); .items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING,
classLoader));
} }
return ConditionOutcome.match(matchMessage); return ConditionOutcome.match(matchMessage);
} }
...@@ -174,72 +135,6 @@ class OnClassCondition extends SpringBootCondition ...@@ -174,72 +135,6 @@ class OnClassCondition extends SpringBootCondition
} }
} }
private List<String> getMatches(Collection<String> candidates, MatchType matchType,
ClassLoader classLoader) {
List<String> matches = new ArrayList<>(candidates.size());
for (String candidate : candidates) {
if (matchType.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
private enum MatchType {
PRESENT {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return isPresent(className, classLoader);
}
},
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
private static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
private static Class<?> forName(String className, ClassLoader classLoader)
throws ClassNotFoundException {
if (classLoader != null) {
return classLoader.loadClass(className);
}
return Class.forName(className);
}
public abstract boolean matches(String className, ClassLoader classLoader);
}
private interface OutcomesResolver { private interface OutcomesResolver {
ConditionOutcome[] resolveOutcomes(); ConditionOutcome[] resolveOutcomes();
...@@ -304,10 +199,12 @@ class OnClassCondition extends SpringBootCondition ...@@ -304,10 +199,12 @@ class OnClassCondition extends SpringBootCondition
ConditionOutcome[] outcomes = new ConditionOutcome[end - start]; ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
for (int i = start; i < end; i++) { for (int i = start; i < end; i++) {
String autoConfigurationClass = autoConfigurationClasses[i]; String autoConfigurationClass = autoConfigurationClasses[i];
Set<String> candidates = autoConfigurationMetadata if (autoConfigurationClass != null) {
.getSet(autoConfigurationClass, "ConditionalOnClass"); Set<String> candidates = autoConfigurationMetadata
if (candidates != null) { .getSet(autoConfigurationClass, "ConditionalOnClass");
outcomes[i - start] = getOutcome(candidates); if (candidates != null) {
outcomes[i - start] = getOutcome(candidates);
}
} }
} }
return outcomes; return outcomes;
...@@ -315,7 +212,7 @@ class OnClassCondition extends SpringBootCondition ...@@ -315,7 +212,7 @@ class OnClassCondition extends SpringBootCondition
private ConditionOutcome getOutcome(Set<String> candidates) { private ConditionOutcome getOutcome(Set<String> candidates) {
try { try {
List<String> missing = getMatches(candidates, MatchType.MISSING, List<String> missing = filter(candidates, ClassNameFilter.MISSING,
this.beanClassLoader); this.beanClassLoader);
if (!missing.isEmpty()) { if (!missing.isEmpty()) {
return ConditionOutcome.noMatch( return ConditionOutcome.noMatch(
......
...@@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.condition; ...@@ -18,6 +18,7 @@ package org.springframework.boot.autoconfigure.condition;
import java.util.Map; import java.util.Map;
import org.springframework.boot.autoconfigure.AutoConfigurationMetadata;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebEnvironment; import org.springframework.boot.web.reactive.context.ConfigurableReactiveWebEnvironment;
import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext; import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext;
...@@ -36,14 +37,60 @@ import org.springframework.web.context.WebApplicationContext; ...@@ -36,14 +37,60 @@ import org.springframework.web.context.WebApplicationContext;
* {@link WebApplicationContext}. * {@link WebApplicationContext}.
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb
* @see ConditionalOnWebApplication * @see ConditionalOnWebApplication
* @see ConditionalOnNotWebApplication * @see ConditionalOnNotWebApplication
*/ */
@Order(Ordered.HIGHEST_PRECEDENCE + 20) @Order(Ordered.HIGHEST_PRECEDENCE + 20)
class OnWebApplicationCondition extends SpringBootCondition { class OnWebApplicationCondition extends FilteringSpringBootCondition {
private static final String WEB_CONTEXT_CLASS = "org.springframework.web.context." private static final String SERVLET_WEB_APPLICATION_CLASS = "org.springframework.web.context.support.GenericWebApplicationContext";
+ "support.GenericWebApplicationContext";
private static final String REACTIVE_WEB_APPLICATION_CLASS = "org.springframework.web.reactive.HandlerResult";
@Override
protected ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
for (int i = 0; i < outcomes.length; i++) {
String autoConfigurationClass = autoConfigurationClasses[i];
if (autoConfigurationClass != null) {
outcomes[i] = getOutcome(autoConfigurationMetadata
.get(autoConfigurationClass, "ConditionalOnWebApplication"));
}
}
return outcomes;
}
private ConditionOutcome getOutcome(String type) {
if (type == null) {
return null;
}
ConditionMessage.Builder message = ConditionMessage
.forCondition(ConditionalOnWebApplication.class);
if (ConditionalOnWebApplication.Type.SERVLET.name().equals(type)) {
if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS,
getBeanClassLoader())) {
return ConditionOutcome.noMatch(
message.didNotFind("servlet web application classes").atAll());
}
}
if (ConditionalOnWebApplication.Type.REACTIVE.name().equals(type)) {
if (!ClassNameFilter.isPresent(REACTIVE_WEB_APPLICATION_CLASS,
getBeanClassLoader())) {
return ConditionOutcome.noMatch(
message.didNotFind("reactive web application classes").atAll());
}
}
if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS,
getBeanClassLoader())
&& !ClassUtils.isPresent(REACTIVE_WEB_APPLICATION_CLASS,
getBeanClassLoader())) {
return ConditionOutcome.noMatch(message
.didNotFind("reactive or servlet web application classes").atAll());
}
return null;
}
@Override @Override
public ConditionOutcome getMatchOutcome(ConditionContext context, public ConditionOutcome getMatchOutcome(ConditionContext context,
...@@ -93,9 +140,10 @@ class OnWebApplicationCondition extends SpringBootCondition { ...@@ -93,9 +140,10 @@ class OnWebApplicationCondition extends SpringBootCondition {
private ConditionOutcome isServletWebApplication(ConditionContext context) { private ConditionOutcome isServletWebApplication(ConditionContext context) {
ConditionMessage.Builder message = ConditionMessage.forCondition(""); ConditionMessage.Builder message = ConditionMessage.forCondition("");
if (!ClassUtils.isPresent(WEB_CONTEXT_CLASS, context.getClassLoader())) { if (!ClassNameFilter.isPresent(SERVLET_WEB_APPLICATION_CLASS,
return ConditionOutcome context.getClassLoader())) {
.noMatch(message.didNotFind("web application classes").atAll()); return ConditionOutcome.noMatch(
message.didNotFind("servlet web application classes").atAll());
} }
if (context.getBeanFactory() != null) { if (context.getBeanFactory() != null) {
String[] scopes = context.getBeanFactory().getRegisteredScopeNames(); String[] scopes = context.getBeanFactory().getRegisteredScopeNames();
...@@ -115,6 +163,11 @@ class OnWebApplicationCondition extends SpringBootCondition { ...@@ -115,6 +163,11 @@ class OnWebApplicationCondition extends SpringBootCondition {
private ConditionOutcome isReactiveWebApplication(ConditionContext context) { private ConditionOutcome isReactiveWebApplication(ConditionContext context) {
ConditionMessage.Builder message = ConditionMessage.forCondition(""); ConditionMessage.Builder message = ConditionMessage.forCondition("");
if (!ClassNameFilter.isPresent(REACTIVE_WEB_APPLICATION_CLASS,
context.getClassLoader())) {
return ConditionOutcome.noMatch(
message.didNotFind("reactive web application classes").atAll());
}
if (context.getEnvironment() instanceof ConfigurableReactiveWebEnvironment) { if (context.getEnvironment() instanceof ConfigurableReactiveWebEnvironment) {
return ConditionOutcome return ConditionOutcome
.match(message.foundExactly("ConfigurableReactiveWebEnvironment")); .match(message.foundExactly("ConfigurableReactiveWebEnvironment"));
......
...@@ -17,7 +17,9 @@ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoCo ...@@ -17,7 +17,9 @@ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoCo
# Auto Configuration Import Filters # Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnClassCondition org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
# Auto Configure # Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
......
...@@ -18,14 +18,15 @@ package org.springframework.boot.autoconfigureprocessor; ...@@ -18,14 +18,15 @@ package org.springframework.boot.autoconfigureprocessor;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.AbstractProcessor;
...@@ -36,10 +37,8 @@ import javax.lang.model.element.AnnotationMirror; ...@@ -36,10 +37,8 @@ import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind; import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType; import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.FileObject; import javax.tools.FileObject;
import javax.tools.StandardLocation; import javax.tools.StandardLocation;
...@@ -52,6 +51,9 @@ import javax.tools.StandardLocation; ...@@ -52,6 +51,9 @@ import javax.tools.StandardLocation;
*/ */
@SupportedAnnotationTypes({ "org.springframework.context.annotation.Configuration", @SupportedAnnotationTypes({ "org.springframework.context.annotation.Configuration",
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass", "org.springframework.boot.autoconfigure.condition.ConditionalOnClass",
"org.springframework.boot.autoconfigure.condition.ConditionalOnBean",
"org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate",
"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication",
"org.springframework.boot.autoconfigure.AutoConfigureBefore", "org.springframework.boot.autoconfigure.AutoConfigureBefore",
"org.springframework.boot.autoconfigure.AutoConfigureAfter", "org.springframework.boot.autoconfigure.AutoConfigureAfter",
"org.springframework.boot.autoconfigure.AutoConfigureOrder" }) "org.springframework.boot.autoconfigure.AutoConfigureOrder" })
...@@ -60,7 +62,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { ...@@ -60,7 +62,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
protected static final String PROPERTIES_PATH = "META-INF/" protected static final String PROPERTIES_PATH = "META-INF/"
+ "spring-autoconfigure-metadata.properties"; + "spring-autoconfigure-metadata.properties";
private Map<String, String> annotations; private final Map<String, String> annotations;
private final Map<String, ValueExtractor> valueExtractors;
private final Properties properties = new Properties(); private final Properties properties = new Properties();
...@@ -68,6 +72,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { ...@@ -68,6 +72,9 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
Map<String, String> annotations = new LinkedHashMap<>(); Map<String, String> annotations = new LinkedHashMap<>();
addAnnotations(annotations); addAnnotations(annotations);
this.annotations = Collections.unmodifiableMap(annotations); this.annotations = Collections.unmodifiableMap(annotations);
Map<String, ValueExtractor> valueExtractors = new LinkedHashMap<>();
addValueExtractors(valueExtractors);
this.valueExtractors = Collections.unmodifiableMap(valueExtractors);
} }
protected void addAnnotations(Map<String, String> annotations) { protected void addAnnotations(Map<String, String> annotations) {
...@@ -75,6 +82,12 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { ...@@ -75,6 +82,12 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
"org.springframework.context.annotation.Configuration"); "org.springframework.context.annotation.Configuration");
annotations.put("ConditionalOnClass", annotations.put("ConditionalOnClass",
"org.springframework.boot.autoconfigure.condition.ConditionalOnClass"); "org.springframework.boot.autoconfigure.condition.ConditionalOnClass");
annotations.put("ConditionalOnBean",
"org.springframework.boot.autoconfigure.condition.ConditionalOnBean");
annotations.put("ConditionalOnSingleCandidate",
"org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate");
annotations.put("ConditionalOnWebApplication",
"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication");
annotations.put("AutoConfigureBefore", annotations.put("AutoConfigureBefore",
"org.springframework.boot.autoconfigure.AutoConfigureBefore"); "org.springframework.boot.autoconfigure.AutoConfigureBefore");
annotations.put("AutoConfigureAfter", annotations.put("AutoConfigureAfter",
...@@ -83,6 +96,18 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { ...@@ -83,6 +96,18 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
"org.springframework.boot.autoconfigure.AutoConfigureOrder"); "org.springframework.boot.autoconfigure.AutoConfigureOrder");
} }
private void addValueExtractors(Map<String, ValueExtractor> attributes) {
attributes.put("Configuration", ValueExtractor.allFrom("value"));
attributes.put("ConditionalOnClass", ValueExtractor.allFrom("value", "name"));
attributes.put("ConditionalOnBean", new OnBeanConditionValueExtractor());
attributes.put("ConditionalOnSingleCandidate",
new OnBeanConditionValueExtractor());
attributes.put("ConditionalOnWebApplication", ValueExtractor.allFrom("type"));
attributes.put("AutoConfigureBefore", ValueExtractor.allFrom("value", "name"));
attributes.put("AutoConfigureAfter", ValueExtractor.allFrom("value", "name"));
attributes.put("AutoConfigureOrder", ValueExtractor.allFrom("value"));
}
@Override @Override
public SourceVersion getSupportedSourceVersion() { public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported(); return SourceVersion.latestSupported();
...@@ -123,10 +148,10 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { ...@@ -123,10 +148,10 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
private void processElement(Element element, String propertyKey, private void processElement(Element element, String propertyKey,
String annotationName) { String annotationName) {
try { try {
String qualifiedName = getQualifiedName(element); String qualifiedName = Elements.getQualifiedName(element);
AnnotationMirror annotation = getAnnotation(element, annotationName); AnnotationMirror annotation = getAnnotation(element, annotationName);
if (qualifiedName != null && annotation != null) { if (qualifiedName != null && annotation != null) {
List<Object> values = getValues(annotation); List<Object> values = getValues(propertyKey, annotation);
this.properties.put(qualifiedName + "." + propertyKey, this.properties.put(qualifiedName + "." + propertyKey,
toCommaDelimitedString(values)); toCommaDelimitedString(values));
this.properties.put(qualifiedName, ""); this.properties.put(qualifiedName, "");
...@@ -158,68 +183,89 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor { ...@@ -158,68 +183,89 @@ public class AutoConfigureAnnotationProcessor extends AbstractProcessor {
return result.toString(); return result.toString();
} }
private List<Object> getValues(AnnotationMirror annotation) { private List<Object> getValues(String propertyKey, AnnotationMirror annotation) {
return annotation.getElementValues().entrySet().stream() ValueExtractor extractor = this.valueExtractors.get(propertyKey);
.filter(this::isNameOrValueAttribute).flatMap(this::getValues) if (extractor == null) {
.collect(Collectors.toList()); return Collections.emptyList();
} }
return extractor.getValues(annotation);
private boolean isNameOrValueAttribute(Entry<? extends ExecutableElement, ?> entry) {
String attributeName = entry.getKey().getSimpleName().toString();
return "name".equals(attributeName) || "value".equals(attributeName);
} }
@SuppressWarnings("unchecked") private void writeProperties() throws IOException {
private Stream<Object> getValues(Entry<?, ? extends AnnotationValue> entry) { if (!this.properties.isEmpty()) {
Object value = entry.getValue().getValue(); FileObject file = this.processingEnv.getFiler()
if (value instanceof List) { .createResource(StandardLocation.CLASS_OUTPUT, "", PROPERTIES_PATH);
return ((List<AnnotationValue>) value).stream() try (OutputStream outputStream = file.openOutputStream()) {
.map((annotation) -> processValue(annotation.getValue())); this.properties.store(outputStream, null);
}
} }
return Stream.of(processValue(value));
} }
private Object processValue(Object value) { @FunctionalInterface
if (value instanceof DeclaredType) { private interface ValueExtractor {
return getQualifiedName(((DeclaredType) value).asElement());
List<Object> getValues(AnnotationMirror annotation);
static ValueExtractor allFrom(String... attributes) {
Set<String> names = new HashSet<>(Arrays.asList(attributes));
return new AbstractValueExtractor() {
@Override
public List<Object> getValues(AnnotationMirror annotation) {
List<Object> result = new ArrayList<>();
annotation.getElementValues().forEach((key, value) -> {
if (names.contains(key.getSimpleName().toString())) {
extractValues(value).forEach(result::add);
}
});
return result;
}
};
} }
return value;
} }
private String getQualifiedName(Element element) { private abstract static class AbstractValueExtractor implements ValueExtractor {
if (element != null) {
TypeElement enclosingElement = getEnclosingTypeElement(element.asType()); @SuppressWarnings("unchecked")
if (enclosingElement != null) { protected Stream<Object> extractValues(AnnotationValue annotationValue) {
return getQualifiedName(enclosingElement) + "$" if (annotationValue == null) {
+ ((DeclaredType) element.asType()).asElement().getSimpleName() return Stream.empty();
.toString();
} }
if (element instanceof TypeElement) { Object value = annotationValue.getValue();
return ((TypeElement) element).getQualifiedName().toString(); if (value instanceof List) {
return ((List<AnnotationValue>) value).stream()
.map((annotation) -> extractValue(annotation.getValue()));
} }
return Stream.of(extractValue(value));
} }
return null;
}
private TypeElement getEnclosingTypeElement(TypeMirror type) { private Object extractValue(Object value) {
if (type instanceof DeclaredType) { if (value instanceof DeclaredType) {
DeclaredType declaredType = (DeclaredType) type; return Elements.getQualifiedName(((DeclaredType) value).asElement());
Element enclosingElement = declaredType.asElement().getEnclosingElement();
if (enclosingElement != null && enclosingElement instanceof TypeElement) {
return (TypeElement) enclosingElement;
} }
return value;
} }
return null;
} }
private void writeProperties() throws IOException { private static class OnBeanConditionValueExtractor extends AbstractValueExtractor {
if (!this.properties.isEmpty()) {
FileObject file = this.processingEnv.getFiler() @Override
.createResource(StandardLocation.CLASS_OUTPUT, "", PROPERTIES_PATH); public List<Object> getValues(AnnotationMirror annotation) {
try (OutputStream outputStream = file.openOutputStream()) { Map<String, AnnotationValue> attributes = new LinkedHashMap<>();
this.properties.store(outputStream, null); annotation.getElementValues().forEach((key, value) -> attributes
.put(key.getSimpleName().toString(), value));
if (attributes.containsKey("name")) {
return Collections.emptyList();
} }
List<Object> result = new ArrayList<>();
extractValues(attributes.get("value")).forEach(result::add);
extractValues(attributes.get("type")).forEach(result::add);
return result;
} }
} }
} }
/*
* Copyright 2012-2018 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.autoconfigureprocessor;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
/**
* Utilities for dealing with {@link Element} classes.
*
* @author Phillip Webb
*/
final class Elements {
private Elements() {
}
static String getQualifiedName(Element element) {
if (element != null) {
TypeElement enclosingElement = getEnclosingTypeElement(element.asType());
if (enclosingElement != null) {
return getQualifiedName(enclosingElement) + "$"
+ ((DeclaredType) element.asType()).asElement().getSimpleName()
.toString();
}
if (element instanceof TypeElement) {
return ((TypeElement) element).getQualifiedName().toString();
}
}
return null;
}
private static TypeElement getEnclosingTypeElement(TypeMirror type) {
if (type instanceof DeclaredType) {
DeclaredType declaredType = (DeclaredType) type;
Element enclosingElement = declaredType.asElement().getEnclosingElement();
if (enclosingElement != null && enclosingElement instanceof TypeElement) {
return (TypeElement) enclosingElement;
}
}
return null;
}
}
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 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.
...@@ -50,18 +50,42 @@ public class AutoConfigureAnnotationProcessorTests { ...@@ -50,18 +50,42 @@ public class AutoConfigureAnnotationProcessorTests {
@Test @Test
public void annotatedClass() throws Exception { public void annotatedClass() throws Exception {
Properties properties = compile(TestClassConfiguration.class); Properties properties = compile(TestClassConfiguration.class);
assertThat(properties).hasSize(3); assertThat(properties).hasSize(6);
assertThat(properties).containsEntry( assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor." "org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration.ConditionalOnClass", + "TestClassConfiguration.ConditionalOnClass",
"java.io.InputStream,org.springframework.boot.autoconfigureprocessor." "java.io.InputStream,org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration$Nested"); + "TestClassConfiguration$Nested");
assertThat(properties).containsKey( assertThat(properties)
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration"); .containsKey("org.springframework.boot.autoconfigureprocessor."
assertThat(properties).containsKey( + "TestClassConfiguration");
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration.Configuration"); assertThat(properties)
assertThat(properties).doesNotContainKey( .containsKey("org.springframework.boot.autoconfigureprocessor."
"org.springframework.boot.autoconfigureprocessor.TestClassConfiguration$Nested"); + "TestClassConfiguration.Configuration");
assertThat(properties)
.doesNotContainKey("org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration$Nested");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration.ConditionalOnBean",
"java.io.OutputStream");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration.ConditionalOnSingleCandidate",
"java.io.OutputStream");
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor."
+ "TestClassConfiguration.ConditionalOnWebApplication",
"SERVLET");
}
@Test
public void annoatedClassWithOnBeanThatHasName() throws Exception {
Properties properties = compile(TestOnBeanWithNameClassConfiguration.class);
assertThat(properties).hasSize(3);
assertThat(properties).containsEntry(
"org.springframework.boot.autoconfigureprocessor.TestOnBeanWithNameClassConfiguration.ConditionalOnBean",
"");
} }
@Test @Test
...@@ -104,7 +128,7 @@ public class AutoConfigureAnnotationProcessorTests { ...@@ -104,7 +128,7 @@ public class AutoConfigureAnnotationProcessorTests {
} }
private Properties compile(Class<?>... types) throws IOException { private Properties compile(Class<?>... types) throws IOException {
TestConditionMetadataAnnotationProcessor processor = new TestConditionMetadataAnnotationProcessor( TestAutoConfigureAnnotationProcessor processor = new TestAutoConfigureAnnotationProcessor(
this.compiler.getOutputLocation()); this.compiler.getOutputLocation());
this.compiler.getTask(types).call(processor); this.compiler.getTask(types).call(processor);
return processor.getWrittenProperties(); return processor.getWrittenProperties();
......
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 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.
...@@ -32,15 +32,18 @@ import javax.annotation.processing.SupportedAnnotationTypes; ...@@ -32,15 +32,18 @@ import javax.annotation.processing.SupportedAnnotationTypes;
@SupportedAnnotationTypes({ @SupportedAnnotationTypes({
"org.springframework.boot.autoconfigureprocessor.TestConfiguration", "org.springframework.boot.autoconfigureprocessor.TestConfiguration",
"org.springframework.boot.autoconfigureprocessor.TestConditionalOnClass", "org.springframework.boot.autoconfigureprocessor.TestConditionalOnClass",
"org.springframework.boot.autoconfigure.condition.TestConditionalOnBean",
"org.springframework.boot.autoconfigure.condition.TestConditionalOnSingleCandidate",
"org.springframework.boot.autoconfigure.condition.TestConditionalOnWebApplication",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureBefore", "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureBefore",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureAfter", "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureAfter",
"org.springframework.boot.autoconfigureprocessor.TestAutoConfigureOrder" }) "org.springframework.boot.autoconfigureprocessor.TestAutoConfigureOrder" })
public class TestConditionMetadataAnnotationProcessor public class TestAutoConfigureAnnotationProcessor
extends AutoConfigureAnnotationProcessor { extends AutoConfigureAnnotationProcessor {
private final File outputLocation; private final File outputLocation;
public TestConditionMetadataAnnotationProcessor(File outputLocation) { public TestAutoConfigureAnnotationProcessor(File outputLocation) {
this.outputLocation = outputLocation; this.outputLocation = outputLocation;
} }
...@@ -48,6 +51,11 @@ public class TestConditionMetadataAnnotationProcessor ...@@ -48,6 +51,11 @@ public class TestConditionMetadataAnnotationProcessor
protected void addAnnotations(Map<String, String> annotations) { protected void addAnnotations(Map<String, String> annotations) {
put(annotations, "Configuration", TestConfiguration.class); put(annotations, "Configuration", TestConfiguration.class);
put(annotations, "ConditionalOnClass", TestConditionalOnClass.class); put(annotations, "ConditionalOnClass", TestConditionalOnClass.class);
put(annotations, "ConditionalOnBean", TestConditionalOnBean.class);
put(annotations, "ConditionalOnSingleCandidate",
TestConditionalOnSingleCandidate.class);
put(annotations, "ConditionalOnWebApplication",
TestConditionalOnWebApplication.class);
put(annotations, "AutoConfigureBefore", TestAutoConfigureBefore.class); put(annotations, "AutoConfigureBefore", TestAutoConfigureBefore.class);
put(annotations, "AutoConfigureAfter", TestAutoConfigureAfter.class); put(annotations, "AutoConfigureAfter", TestAutoConfigureAfter.class);
put(annotations, "AutoConfigureOrder", TestAutoConfigureOrder.class); put(annotations, "AutoConfigureOrder", TestAutoConfigureOrder.class);
......
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 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,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigureprocessor; package org.springframework.boot.autoconfigureprocessor;
import org.springframework.boot.autoconfigureprocessor.TestConditionalOnWebApplication.Type;
/** /**
* Test configuration with an annotated class. * Test configuration with an annotated class.
* *
...@@ -23,6 +25,9 @@ package org.springframework.boot.autoconfigureprocessor; ...@@ -23,6 +25,9 @@ package org.springframework.boot.autoconfigureprocessor;
*/ */
@TestConfiguration @TestConfiguration
@TestConditionalOnClass(name = "java.io.InputStream", value = TestClassConfiguration.Nested.class) @TestConditionalOnClass(name = "java.io.InputStream", value = TestClassConfiguration.Nested.class)
@TestConditionalOnBean(type = "java.io.OutputStream")
@TestConditionalOnSingleCandidate(type = "java.io.OutputStream")
@TestConditionalOnWebApplication(type = Type.SERVLET)
public class TestClassConfiguration { public class TestClassConfiguration {
@TestAutoConfigureOrder @TestAutoConfigureOrder
......
/*
* Copyright 2012-2018 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.autoconfigureprocessor;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Alternative to Spring Boot's {@code ConditionalOnBean} for testing (removes the need
* for a dependency on the real annotation).
*
* @author Phillip Webb
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestConditionalOnBean {
Class<?>[] value() default {};
String[] type() default {};
Class<? extends Annotation>[] annotation() default {};
String[] name() default {};
}
/*
* Copyright 2012-2018 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.autoconfigureprocessor;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Alternative to Spring Boot's {@code ConditionalOnSingleCandidate} for testing (removes
* the need for a dependency on the real annotation).
*
* @author Phillip Webb
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestConditionalOnSingleCandidate {
Class<?> value() default Object.class;
String type() default "";
}
/*
* Copyright 2012-2018 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.autoconfigureprocessor;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Alternative to Spring Boot's {@code @ConditionalOnWebApplication} for testing (removes
* the need for a dependency on the real annotation).
*
* @author Phillip Webb
*/
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TestConditionalOnWebApplication {
Type type() default Type.ANY;
enum Type {
ANY, SERVLET, REACTIVE
}
}
/*
* Copyright 2012-2018 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.autoconfigureprocessor;
/**
* Test configuration with an annotated class.
*
* @author Phillip Webb
*/
@TestConfiguration
@TestConditionalOnBean(name = "test", type = "java.io.OutputStream")
public class TestOnBeanWithNameClassConfiguration {
}
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