Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.github.trang.autoconfigure.condition.OnBeansCondition Maven / Gradle / Ivy
/*
* Copyright 2012-2017 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 com.github.trang.autoconfigure.condition;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.MethodMetadata;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* {@link Condition} that checks for the presence or absence of specific beans.
*
* @author trang
*/
@Order
class OnBeansCondition extends SpringBootCondition implements ConfigurationCondition {
/**
* Bean definition attribute name for factory beans to signal their product type (if
* known and it can't be deduced from the factory bean class).
*/
public static final String FACTORY_BEAN_OBJECT_TYPE = BeanTypeRegistry.FACTORY_BEAN_OBJECT_TYPE;
@Override
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage matchMessage = ConditionMessage.empty();
if (metadata.isAnnotated(ConditionalOnBeans.class.getName())) {
BeanSearchSpec spec = new BeanSearchSpec(context, metadata, ConditionalOnBeans.class);
boolean matching = isMatchingBeans(context, spec);
if (!matching) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(
ConditionalOnBeans.class, spec).didNotFind("all beans").atAll());
}
matchMessage = matchMessage.andCondition(ConditionalOnBeans.class, spec)
.found("all beans").atAll();
}
if (metadata.isAnnotated(ConditionalOnMissingBeans.class.getName())) {
BeanSearchSpec spec = new BeanSearchSpec(context, metadata, ConditionalOnMissingBeans.class);
boolean matching = isNonMatchingBeans(context, spec);
if (!matching) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(
ConditionalOnMissingBeans.class, spec).found("beans").atAll());
}
matchMessage = matchMessage.andCondition(ConditionalOnMissingBeans.class, spec)
.didNotFind("any beans").atAll();
}
return ConditionOutcome.match(matchMessage);
}
private boolean isMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beans.getStrategy() == SearchStrategy.PARENTS || beans.getStrategy() == SearchStrategy.ANCESTORS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, "Unable to use SearchStrategy.PARENTS");
beanFactory = (ConfigurableListableBeanFactory) parent;
}
if (beanFactory == null) {
return false;
}
boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
for (String type : beans.getTypes()) {
Collection beanNames = getBeanNamesForType(beanFactory, type, context.getClassLoader(), considerHierarchy);
if (beanNames.isEmpty()) {
return false;
}
}
for (String annotation : beans.getAnnotations()) {
Collection beanNames = getBeanNamesForAnnotation(beanFactory, annotation, context.getClassLoader(), considerHierarchy);
if (beanNames.isEmpty()) {
return false;
}
}
for (String beanName : beans.getNames()) {
if (!containsBean(beanFactory, beanName, considerHierarchy)) {
return false;
}
}
return true;
}
private boolean isNonMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
if (beans.getStrategy() == SearchStrategy.PARENTS || beans.getStrategy() == SearchStrategy.ANCESTORS) {
BeanFactory parent = beanFactory.getParentBeanFactory();
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, "Unable to use SearchStrategy.PARENTS");
beanFactory = (ConfigurableListableBeanFactory) parent;
}
if (beanFactory == null) {
return false;
}
boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
for (String type : beans.getTypes()) {
Collection beanNames = getBeanNamesForType(beanFactory, type, context.getClassLoader(), considerHierarchy);
if (!beanNames.isEmpty()) {
return false;
}
}
for (String annotation : beans.getAnnotations()) {
Collection beanNames = getBeanNamesForAnnotation(beanFactory, annotation, context.getClassLoader(), considerHierarchy);
if (!beanNames.isEmpty()) {
return false;
}
}
for (String beanName : beans.getNames()) {
if (containsBean(beanFactory, beanName, considerHierarchy)) {
return false;
}
}
return true;
}
private boolean containsBean(ConfigurableListableBeanFactory beanFactory,
String beanName, boolean considerHierarchy) {
if (considerHierarchy) {
return beanFactory.containsBean(beanName);
}
return beanFactory.containsLocalBean(beanName);
}
private Collection getBeanNamesForType(ListableBeanFactory beanFactory, String type,
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
try {
Set result = new LinkedHashSet<>();
collectBeanNamesForType(result, beanFactory,
ClassUtils.forName(type, classLoader), considerHierarchy);
return result;
} catch (ClassNotFoundException | NoClassDefFoundError ex) {
return Collections.emptySet();
}
}
private void collectBeanNamesForType(Set result, ListableBeanFactory beanFactory, Class> type,
boolean considerHierarchy) {
result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory();
if (parent instanceof ListableBeanFactory) {
collectBeanNamesForType(result, (ListableBeanFactory) parent, type, considerHierarchy);
}
}
}
private Collection getBeanNamesForAnnotation(ConfigurableListableBeanFactory beanFactory, String type,
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
Set names = new HashSet<>();
try {
@SuppressWarnings("unchecked")
Class extends Annotation> annotationType = (Class extends Annotation>) ClassUtils.forName(type, classLoader);
collectBeanNamesForAnnotation(names, beanFactory, annotationType, considerHierarchy);
} catch (ClassNotFoundException e) {
// Continue
}
return names;
}
private void collectBeanNamesForAnnotation(Set names, ListableBeanFactory beanFactory,
Class extends Annotation> annotationType, boolean considerHierarchy) {
names.addAll(BeanTypeRegistry.get(beanFactory).getNamesForAnnotation(annotationType));
if (considerHierarchy) {
BeanFactory parent = ((HierarchicalBeanFactory) beanFactory).getParentBeanFactory();
if (parent instanceof ListableBeanFactory) {
collectBeanNamesForAnnotation(names, (ListableBeanFactory) parent, annotationType, considerHierarchy);
}
}
}
private boolean hasSingleAutowireCandidate(ConfigurableListableBeanFactory beanFactory,
List beanNames, boolean considerHierarchy) {
return (beanNames.size() == 1 || getPrimaryBeans(beanFactory, beanNames, considerHierarchy).size() == 1);
}
private List getPrimaryBeans(ConfigurableListableBeanFactory beanFactory,
List beanNames, boolean considerHierarchy) {
List primaryBeans = new ArrayList();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = findBeanDefinition(beanFactory, beanName, considerHierarchy);
if (beanDefinition != null && beanDefinition.isPrimary()) {
primaryBeans.add(beanName);
}
}
return primaryBeans;
}
private BeanDefinition findBeanDefinition(ConfigurableListableBeanFactory beanFactory,
String beanName, boolean considerHierarchy) {
if (beanFactory.containsBeanDefinition(beanName)) {
return beanFactory.getBeanDefinition(beanName);
}
if (considerHierarchy && beanFactory.getParentBeanFactory() instanceof ConfigurableListableBeanFactory) {
return findBeanDefinition(((ConfigurableListableBeanFactory) beanFactory.getParentBeanFactory()), beanName, considerHierarchy);
}
return null;
}
private static class BeanSearchSpec {
private final Class> annotationType;
private final List names = new ArrayList<>();
private final List types = new ArrayList<>();
private final List annotations = new ArrayList<>();
private final List ignoredTypes = new ArrayList<>();
private final SearchStrategy strategy;
BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata, Class> annotationType) {
this.annotationType = annotationType;
MultiValueMap attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
collect(attributes, "name", this.names);
collect(attributes, "value", this.types);
collect(attributes, "type", this.types);
collect(attributes, "annotation", this.annotations);
collect(attributes, "ignored", this.ignoredTypes);
collect(attributes, "ignoredType", this.ignoredTypes);
this.strategy = (SearchStrategy) metadata.getAnnotationAttributes(annotationType.getName()).get("search");
BeanTypeDeductionException deductionException = null;
try {
if (this.types.isEmpty() && this.names.isEmpty()) {
addDeducedBeanType(context, metadata, this.types);
}
} catch (BeanTypeDeductionException ex) {
deductionException = ex;
}
validate(deductionException);
}
protected void validate(BeanTypeDeductionException ex) {
if (!hasAtLeastOne(this.types, this.names, this.annotations)) {
String message = annotationName() + " did not specify a bean using type, name or annotation";
if (ex == null) {
throw new IllegalStateException(message);
}
throw new IllegalStateException(message + " and the attempt to deduce the bean's type failed", ex);
}
}
private boolean hasAtLeastOne(List>... lists) {
for (List> list : lists) {
if (!list.isEmpty()) {
return true;
}
}
return false;
}
protected String annotationName() {
return "@" + ClassUtils.getShortName(this.annotationType);
}
protected void collect(MultiValueMap attributes, String key, List destination) {
List> values = attributes.get(key);
if (values != null) {
for (Object value : values) {
if (value instanceof String[]) {
Collections.addAll(destination, (String[]) value);
} else {
destination.add((String) value);
}
}
}
}
private void addDeducedBeanType(ConditionContext context,
AnnotatedTypeMetadata metadata, final List beanTypes) {
if (metadata instanceof MethodMetadata && metadata.isAnnotated(Bean.class.getName())) {
addDeducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata, beanTypes);
}
}
private void addDeducedBeanTypeForBeanMethod(ConditionContext context,
MethodMetadata metadata, final List beanTypes) {
try {
// We should be safe to load at this point since we are in the
// REGISTER_BEAN phase
Class> returnType = ClassUtils.forName(metadata.getReturnTypeName(), context.getClassLoader());
beanTypes.add(returnType.getName());
} catch (Throwable ex) {
throw new BeanTypeDeductionException(metadata.getDeclaringClassName(), metadata.getMethodName(), ex);
}
}
public SearchStrategy getStrategy() {
return (this.strategy != null ? this.strategy : SearchStrategy.ALL);
}
public List getNames() {
return this.names;
}
public List getTypes() {
return this.types;
}
public List getAnnotations() {
return this.annotations;
}
public List getIgnoredTypes() {
return this.ignoredTypes;
}
@Override
public String toString() {
StringBuilder string = new StringBuilder();
string.append("(");
if (!this.names.isEmpty()) {
string.append("names: ");
string.append(StringUtils.collectionToCommaDelimitedString(this.names));
if (!this.types.isEmpty()) {
string.append("; ");
}
}
if (!this.types.isEmpty()) {
string.append("types: ");
string.append(StringUtils.collectionToCommaDelimitedString(this.types));
}
string.append("; SearchStrategy: ");
string.append(this.strategy.toString().toLowerCase());
string.append(")");
return string.toString();
}
}
static final class BeanTypeDeductionException extends RuntimeException {
private BeanTypeDeductionException(String className, String beanMethodName, Throwable cause) {
super("Failed to deduce bean type for " + className + "." + beanMethodName, cause);
}
}
}