Relocate projects to spring-boot-project
Move projects to better reflect the way that Spring Boot is released. The following projects are under `spring-boot-project`: - `spring-boot` - `spring-boot-autoconfigure` - `spring-boot-tools` - `spring-boot-starters` - `spring-boot-actuator` - `spring-boot-actuator-autoconfigure` - `spring-boot-test` - `spring-boot-test-autoconfigure` - `spring-boot-devtools` - `spring-boot-cli` - `spring-boot-docs` See gh-9316
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.boot.jdbc.DatabaseDriver;
|
||||
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.jdbc.support.JdbcUtils;
|
||||
import org.springframework.jdbc.support.MetaDataAccessException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base class used for database initialization.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public abstract class AbstractDatabaseInitializer {
|
||||
|
||||
private static final String PLATFORM_PLACEHOLDER = "@@platform@@";
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
|
||||
protected AbstractDatabaseInitializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
Assert.notNull(dataSource, "DataSource must not be null");
|
||||
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
|
||||
this.dataSource = dataSource;
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
protected void initialize() {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
|
||||
String schemaLocation = getSchemaLocation();
|
||||
if (schemaLocation.contains(PLATFORM_PLACEHOLDER)) {
|
||||
String platform = getDatabaseName();
|
||||
schemaLocation = schemaLocation.replace(PLATFORM_PLACEHOLDER, platform);
|
||||
}
|
||||
populator.addScript(this.resourceLoader.getResource(schemaLocation));
|
||||
populator.setContinueOnError(true);
|
||||
DatabasePopulatorUtils.execute(populator, this.dataSource);
|
||||
}
|
||||
|
||||
private boolean isEnabled() {
|
||||
if (getMode() == DatabaseInitializationMode.NEVER) {
|
||||
return false;
|
||||
}
|
||||
if (getMode() == DatabaseInitializationMode.EMBEDDED
|
||||
&& !EmbeddedDatabaseConnection.isEmbedded(this.dataSource)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract DatabaseInitializationMode getMode();
|
||||
|
||||
protected abstract String getSchemaLocation();
|
||||
|
||||
protected String getDatabaseName() {
|
||||
try {
|
||||
String productName = JdbcUtils.commonDatabaseName(JdbcUtils
|
||||
.extractDatabaseMetaData(this.dataSource, "getDatabaseProductName")
|
||||
.toString());
|
||||
DatabaseDriver databaseDriver = DatabaseDriver.fromProductName(productName);
|
||||
if (databaseDriver == DatabaseDriver.UNKNOWN) {
|
||||
throw new IllegalStateException("Unable to detect database type");
|
||||
}
|
||||
return databaseDriver.getId();
|
||||
}
|
||||
catch (MetaDataAccessException ex) {
|
||||
throw new IllegalStateException("Unable to detect database type", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for a {@link BeanFactoryPostProcessor} that can be used to
|
||||
* dynamically declare that all beans of a specific type should depend on one or more
|
||||
* specific beans.
|
||||
*
|
||||
* @author Marcel Overdijk
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
* @see BeanDefinition#setDependsOn(String[])
|
||||
*/
|
||||
public abstract class AbstractDependsOnBeanFactoryPostProcessor
|
||||
implements BeanFactoryPostProcessor {
|
||||
|
||||
private final Class<?> beanClass;
|
||||
|
||||
private final Class<? extends FactoryBean<?>> factoryBeanClass;
|
||||
|
||||
private final String[] dependsOn;
|
||||
|
||||
protected AbstractDependsOnBeanFactoryPostProcessor(Class<?> beanClass,
|
||||
Class<? extends FactoryBean<?>> factoryBeanClass, String... dependsOn) {
|
||||
this.beanClass = beanClass;
|
||||
this.factoryBeanClass = factoryBeanClass;
|
||||
this.dependsOn = dependsOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
|
||||
for (String beanName : getBeanNames(beanFactory)) {
|
||||
BeanDefinition definition = getBeanDefinition(beanName, beanFactory);
|
||||
String[] dependencies = definition.getDependsOn();
|
||||
for (String bean : this.dependsOn) {
|
||||
dependencies = StringUtils.addStringToArray(dependencies, bean);
|
||||
}
|
||||
definition.setDependsOn(dependencies);
|
||||
}
|
||||
}
|
||||
|
||||
private Iterable<String> getBeanNames(ListableBeanFactory beanFactory) {
|
||||
Set<String> names = new HashSet<>();
|
||||
names.addAll(Arrays.asList(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
beanFactory, this.beanClass, true, false)));
|
||||
for (String factoryBeanName : BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
|
||||
beanFactory, this.factoryBeanClass, true, false)) {
|
||||
names.add(BeanFactoryUtils.transformedBeanName(factoryBeanName));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
private static BeanDefinition getBeanDefinition(String beanName,
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
try {
|
||||
return beanFactory.getBeanDefinition(beanName);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
BeanFactory parentBeanFactory = beanFactory.getParentBeanFactory();
|
||||
if (parentBeanFactory instanceof ConfigurableListableBeanFactory) {
|
||||
return getBeanDefinition(beanName,
|
||||
(ConfigurableListableBeanFactory) parentBeanFactory);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.filter.TypeFilter;
|
||||
|
||||
/**
|
||||
* A {@link TypeFilter} implementation that matches registered auto-configuration classes.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
private volatile List<String> autoConfigurations;
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean match(MetadataReader metadataReader,
|
||||
MetadataReaderFactory metadataReaderFactory) throws IOException {
|
||||
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
|
||||
}
|
||||
|
||||
private boolean isConfiguration(MetadataReader metadataReader) {
|
||||
return metadataReader.getAnnotationMetadata()
|
||||
.isAnnotated(Configuration.class.getName());
|
||||
}
|
||||
|
||||
private boolean isAutoConfiguration(MetadataReader metadataReader) {
|
||||
return getAutoConfigurations()
|
||||
.contains(metadataReader.getClassMetadata().getClassName());
|
||||
}
|
||||
|
||||
protected List<String> getAutoConfigurations() {
|
||||
if (this.autoConfigurations == null) {
|
||||
this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(
|
||||
EnableAutoConfiguration.class, this.beanClassLoader);
|
||||
}
|
||||
return this.autoConfigurations;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EventObject;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Event fired when auto-configuration classes are imported.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class AutoConfigurationImportEvent extends EventObject {
|
||||
|
||||
private final List<String> candidateConfigurations;
|
||||
|
||||
private final Set<String> exclusions;
|
||||
|
||||
public AutoConfigurationImportEvent(Object source,
|
||||
List<String> candidateConfigurations, Set<String> exclusions) {
|
||||
super(source);
|
||||
this.candidateConfigurations = Collections
|
||||
.unmodifiableList(candidateConfigurations);
|
||||
this.exclusions = Collections.unmodifiableSet(exclusions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the auto-configuration candidate configurations that are going to be
|
||||
* imported.
|
||||
* @return the auto-configuration candidates
|
||||
*/
|
||||
public List<String> getCandidateConfigurations() {
|
||||
return this.candidateConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the exclusions that were applied.
|
||||
* @return the exclusions applied
|
||||
*/
|
||||
public Set<String> getExclusions() {
|
||||
return this.exclusions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
||||
/**
|
||||
* Filter that can be registered in {@code spring.factories} to limit the
|
||||
* auto-configuration classes considered. This interface is designed to allow fast removal
|
||||
* of auto-configuration classes before their bytecode is even read.
|
||||
* <p>
|
||||
* An {@link AutoConfigurationImportFilter} may implement any of the following
|
||||
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
|
||||
* methods will be called prior to {@link #match}:
|
||||
* <ul>
|
||||
* <li>{@link EnvironmentAware}</li>
|
||||
* <li>{@link BeanFactoryAware}</li>
|
||||
* <li>{@link BeanClassLoaderAware}</li>
|
||||
* <li>{@link ResourceLoaderAware}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AutoConfigurationImportFilter {
|
||||
|
||||
/**
|
||||
* Apply the filter to the given auto-configuration class candidates.
|
||||
* @param autoConfigurationClasses the auto-configuration classes being considered.
|
||||
* Implementations should not change the values in this array.
|
||||
* @param autoConfigurationMetadata access to the meta-data generated by the
|
||||
* auto-configure annotation processor
|
||||
* @return a boolean array indicating which of the auto-configuration classes should
|
||||
* be imported. The returned array must be the same size as the incoming
|
||||
* {@code autoConfigurationClasses} parameter. Entries containing {@code false} will
|
||||
* not be imported.
|
||||
*/
|
||||
boolean[] match(String[] autoConfigurationClasses,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.util.EventListener;
|
||||
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
|
||||
/**
|
||||
* Listener that can be registered with {@code spring.factories} to receive details of
|
||||
* imported auto-configurations.
|
||||
* <p>
|
||||
* An {@link AutoConfigurationImportListener} may implement any of the following
|
||||
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
|
||||
* methods will be called prior to
|
||||
* {@link #onAutoConfigurationImportEvent(AutoConfigurationImportEvent)}:
|
||||
* <ul>
|
||||
* <li>{@link EnvironmentAware}</li>
|
||||
* <li>{@link BeanFactoryAware}</li>
|
||||
* <li>{@link BeanClassLoaderAware}</li>
|
||||
* <li>{@link ResourceLoaderAware}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface AutoConfigurationImportListener extends EventListener {
|
||||
|
||||
/**
|
||||
* Handle an auto-configuration import event.
|
||||
* @param event the event to respond to
|
||||
*/
|
||||
void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.Aware;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.annotation.DeferredImportSelector;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link DeferredImportSelector} to handle {@link EnableAutoConfiguration
|
||||
* auto-configuration}. This class can also be subclassed if a custom variant of
|
||||
* {@link EnableAutoConfiguration @EnableAutoConfiguration}. is needed.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
* @since 1.3.0
|
||||
* @see EnableAutoConfiguration
|
||||
*/
|
||||
public class AutoConfigurationImportSelector
|
||||
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
|
||||
BeanFactoryAware, EnvironmentAware, Ordered {
|
||||
|
||||
private static final String[] NO_IMPORTS = {};
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(AutoConfigurationImportSelector.class);
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
private ResourceLoader resourceLoader;
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata annotationMetadata) {
|
||||
if (!isEnabled(annotationMetadata)) {
|
||||
return NO_IMPORTS;
|
||||
}
|
||||
try {
|
||||
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
|
||||
.loadMetadata(this.beanClassLoader);
|
||||
AnnotationAttributes attributes = getAttributes(annotationMetadata);
|
||||
List<String> configurations = getCandidateConfigurations(annotationMetadata,
|
||||
attributes);
|
||||
configurations = removeDuplicates(configurations);
|
||||
configurations = sort(configurations, autoConfigurationMetadata);
|
||||
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
|
||||
checkExcludedClasses(configurations, exclusions);
|
||||
configurations.removeAll(exclusions);
|
||||
configurations = filter(configurations, autoConfigurationMetadata);
|
||||
fireAutoConfigurationImportEvents(configurations, exclusions);
|
||||
return configurations.toArray(new String[configurations.size()]);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isEnabled(AnnotationMetadata metadata) {
|
||||
if (getClass().equals(AutoConfigurationImportSelector.class)) {
|
||||
return getEnvironment().getProperty(
|
||||
EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class,
|
||||
true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the appropriate {@link AnnotationAttributes} from the
|
||||
* {@link AnnotationMetadata}. By default this method will return attributes for
|
||||
* {@link #getAnnotationClass()}.
|
||||
* @param metadata the annotation metadata
|
||||
* @return annotation attributes
|
||||
*/
|
||||
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
|
||||
String name = getAnnotationClass().getName();
|
||||
AnnotationAttributes attributes = AnnotationAttributes
|
||||
.fromMap(metadata.getAnnotationAttributes(name, true));
|
||||
Assert.notNull(attributes,
|
||||
"No auto-configuration attributes found. Is " + metadata.getClassName()
|
||||
+ " annotated with " + ClassUtils.getShortName(name) + "?");
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the source annotation class used by the selector.
|
||||
* @return the annotation class
|
||||
*/
|
||||
protected Class<?> getAnnotationClass() {
|
||||
return EnableAutoConfiguration.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the auto-configuration class names that should be considered. By default
|
||||
* this method will load candidates using {@link SpringFactoriesLoader} with
|
||||
* {@link #getSpringFactoriesLoaderFactoryClass()}.
|
||||
* @param metadata the source metadata
|
||||
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
|
||||
* attributes}
|
||||
* @return a list of candidate configurations
|
||||
*/
|
||||
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
|
||||
AnnotationAttributes attributes) {
|
||||
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
|
||||
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
|
||||
Assert.notEmpty(configurations,
|
||||
"No auto configuration classes found in META-INF/spring.factories. If you "
|
||||
+ "are using a custom packaging, make sure that file is correct.");
|
||||
return configurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class used by {@link SpringFactoriesLoader} to load configuration
|
||||
* candidates.
|
||||
* @return the factory class
|
||||
*/
|
||||
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
|
||||
return EnableAutoConfiguration.class;
|
||||
}
|
||||
|
||||
private void checkExcludedClasses(List<String> configurations,
|
||||
Set<String> exclusions) {
|
||||
List<String> invalidExcludes = new ArrayList<>(exclusions.size());
|
||||
for (String exclusion : exclusions) {
|
||||
if (ClassUtils.isPresent(exclusion, getClass().getClassLoader())
|
||||
&& !configurations.contains(exclusion)) {
|
||||
invalidExcludes.add(exclusion);
|
||||
}
|
||||
}
|
||||
if (!invalidExcludes.isEmpty()) {
|
||||
handleInvalidExcludes(invalidExcludes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle any invalid excludes that have been specified.
|
||||
* @param invalidExcludes the list of invalid excludes (will always have at least one
|
||||
* element)
|
||||
*/
|
||||
protected void handleInvalidExcludes(List<String> invalidExcludes) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
for (String exclude : invalidExcludes) {
|
||||
message.append("\t- ").append(exclude).append(String.format("%n"));
|
||||
}
|
||||
throw new IllegalStateException(String
|
||||
.format("The following classes could not be excluded because they are"
|
||||
+ " not auto-configuration classes:%n%s", message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return any exclusions that limit the candidate configurations.
|
||||
* @param metadata the source metadata
|
||||
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
|
||||
* attributes}
|
||||
* @return exclusions or an empty set
|
||||
*/
|
||||
protected Set<String> getExclusions(AnnotationMetadata metadata,
|
||||
AnnotationAttributes attributes) {
|
||||
Set<String> excluded = new LinkedHashSet<>();
|
||||
excluded.addAll(asList(attributes, "exclude"));
|
||||
excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
|
||||
excluded.addAll(getExcludeAutoConfigurationsProperty());
|
||||
return excluded;
|
||||
}
|
||||
|
||||
private List<String> getExcludeAutoConfigurationsProperty() {
|
||||
String name = "spring.autoconfigure.exclude";
|
||||
if (getEnvironment() instanceof ConfigurableEnvironment) {
|
||||
Binder binder = Binder.get(getEnvironment());
|
||||
return binder.bind(name, String[].class).map(Arrays::asList)
|
||||
.orElse(Collections.emptyList());
|
||||
}
|
||||
String[] excludes = getEnvironment().getProperty(name, String[].class);
|
||||
return (excludes == null ? Collections.emptyList() : Arrays.asList(excludes));
|
||||
}
|
||||
|
||||
private List<String> sort(List<String> configurations,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) throws IOException {
|
||||
configurations = new AutoConfigurationSorter(getMetadataReaderFactory(),
|
||||
autoConfigurationMetadata).getInPriorityOrder(configurations);
|
||||
return configurations;
|
||||
}
|
||||
|
||||
private List<String> filter(List<String> configurations,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
long startTime = System.nanoTime();
|
||||
String[] candidates = configurations.toArray(new String[configurations.size()]);
|
||||
boolean[] skip = new boolean[candidates.length];
|
||||
boolean skipped = false;
|
||||
for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
|
||||
invokeAwareMethods(filter);
|
||||
boolean[] match = filter.match(candidates, autoConfigurationMetadata);
|
||||
for (int i = 0; i < match.length; i++) {
|
||||
if (!match[i]) {
|
||||
skip[i] = true;
|
||||
skipped = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!skipped) {
|
||||
return configurations;
|
||||
}
|
||||
List<String> result = new ArrayList<>(candidates.length);
|
||||
for (int i = 0; i < candidates.length; i++) {
|
||||
if (!skip[i]) {
|
||||
result.add(candidates[i]);
|
||||
}
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
int numberFiltered = configurations.size() - result.size();
|
||||
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
|
||||
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)
|
||||
+ " ms");
|
||||
}
|
||||
return new ArrayList<>(result);
|
||||
}
|
||||
|
||||
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
|
||||
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class,
|
||||
this.beanClassLoader);
|
||||
}
|
||||
|
||||
private MetadataReaderFactory getMetadataReaderFactory() {
|
||||
try {
|
||||
return getBeanFactory().getBean(
|
||||
SharedMetadataReaderFactoryContextInitializer.BEAN_NAME,
|
||||
MetadataReaderFactory.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
return new CachingMetadataReaderFactory(this.resourceLoader);
|
||||
}
|
||||
}
|
||||
|
||||
protected final <T> List<T> removeDuplicates(List<T> list) {
|
||||
return new ArrayList<>(new LinkedHashSet<>(list));
|
||||
}
|
||||
|
||||
protected final List<String> asList(AnnotationAttributes attributes, String name) {
|
||||
String[] value = attributes.getStringArray(name);
|
||||
return Arrays.asList(value == null ? new String[0] : value);
|
||||
}
|
||||
|
||||
private void fireAutoConfigurationImportEvents(List<String> configurations,
|
||||
Set<String> exclusions) {
|
||||
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
|
||||
if (!listeners.isEmpty()) {
|
||||
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this,
|
||||
configurations, exclusions);
|
||||
for (AutoConfigurationImportListener listener : listeners) {
|
||||
invokeAwareMethods(listener);
|
||||
listener.onAutoConfigurationImportEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected List<AutoConfigurationImportListener> getAutoConfigurationImportListeners() {
|
||||
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportListener.class,
|
||||
this.beanClassLoader);
|
||||
}
|
||||
|
||||
private void invokeAwareMethods(Object instance) {
|
||||
if (instance instanceof Aware) {
|
||||
if (instance instanceof BeanClassLoaderAware) {
|
||||
((BeanClassLoaderAware) instance)
|
||||
.setBeanClassLoader(this.beanClassLoader);
|
||||
}
|
||||
if (instance instanceof BeanFactoryAware) {
|
||||
((BeanFactoryAware) instance).setBeanFactory(this.beanFactory);
|
||||
}
|
||||
if (instance instanceof EnvironmentAware) {
|
||||
((EnvironmentAware) instance).setEnvironment(this.environment);
|
||||
}
|
||||
if (instance instanceof ResourceLoaderAware) {
|
||||
((ResourceLoaderAware) instance).setResourceLoader(this.resourceLoader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
|
||||
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
protected final ConfigurableListableBeanFactory getBeanFactory() {
|
||||
return this.beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.beanClassLoader = classLoader;
|
||||
}
|
||||
|
||||
protected ClassLoader getBeanClassLoader() {
|
||||
return this.beanClassLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
protected final Environment getEnvironment() {
|
||||
return this.environment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
protected final ResourceLoader getResourceLoader() {
|
||||
return this.resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Ordered.LOWEST_PRECEDENCE - 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides access to meta-data written by the auto-configure annotation processor.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public interface AutoConfigurationMetadata {
|
||||
|
||||
/**
|
||||
* Return {@code true} if the specified class name was processed by the annotation
|
||||
* processor.
|
||||
* @param className the source class
|
||||
* @return if the class was processed
|
||||
*/
|
||||
boolean wasProcessed(String className);
|
||||
|
||||
/**
|
||||
* Get an {@link Integer} value from the meta-data.
|
||||
* @param className the source class
|
||||
* @param key the meta-data key
|
||||
* @return the meta-data value or {@code null}
|
||||
*/
|
||||
Integer getInteger(String className, String key);
|
||||
|
||||
/**
|
||||
* Get an {@link Integer} value from the meta-data.
|
||||
* @param className the source class
|
||||
* @param key the meta-data key
|
||||
* @param defaultValue the default value
|
||||
* @return the meta-data value or {@code defaultValue}
|
||||
*/
|
||||
Integer getInteger(String className, String key, Integer defaultValue);
|
||||
|
||||
/**
|
||||
* Get a {@link Set} value from the meta-data.
|
||||
* @param className the source class
|
||||
* @param key the meta-data key
|
||||
* @return the meta-data value or {@code null}
|
||||
*/
|
||||
Set<String> getSet(String className, String key);
|
||||
|
||||
/**
|
||||
* Get a {@link Set} value from the meta-data.
|
||||
* @param className the source class
|
||||
* @param key the meta-data key
|
||||
* @param defaultValue the default value
|
||||
* @return the meta-data value or {@code defaultValue}
|
||||
*/
|
||||
Set<String> getSet(String className, String key, Set<String> defaultValue);
|
||||
|
||||
/**
|
||||
* Get an {@link String} value from the meta-data.
|
||||
* @param className the source class
|
||||
* @param key the meta-data key
|
||||
* @return the meta-data value or {@code null}
|
||||
*/
|
||||
String get(String className, String key);
|
||||
|
||||
/**
|
||||
* Get an {@link String} value from the meta-data.
|
||||
* @param className the source class
|
||||
* @param key the meta-data key
|
||||
* @param defaultValue the default value
|
||||
* @return the meta-data value or {@code defaultValue}
|
||||
*/
|
||||
String get(String className, String key, String defaultValue);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.core.io.support.PropertiesLoaderUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Internal utility used to load {@link AutoConfigurationMetadata}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
final class AutoConfigurationMetadataLoader {
|
||||
|
||||
protected static final String PATH = "META-INF/"
|
||||
+ "spring-autoconfigure-metadata.properties";
|
||||
|
||||
private AutoConfigurationMetadataLoader() {
|
||||
}
|
||||
|
||||
public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) {
|
||||
return loadMetadata(classLoader, PATH);
|
||||
}
|
||||
|
||||
static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) {
|
||||
try {
|
||||
Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(path)
|
||||
: ClassLoader.getSystemResources(path));
|
||||
Properties properties = new Properties();
|
||||
while (urls.hasMoreElements()) {
|
||||
properties.putAll(PropertiesLoaderUtils
|
||||
.loadProperties(new UrlResource(urls.nextElement())));
|
||||
}
|
||||
return loadMetadata(properties);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to load @ConditionalOnClass location [" + path + "]", ex);
|
||||
}
|
||||
}
|
||||
|
||||
static AutoConfigurationMetadata loadMetadata(Properties properties) {
|
||||
return new PropertiesAutoConfigurationMetadata(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AutoConfigurationMetadata} implementation backed by a properties file.
|
||||
*/
|
||||
private static class PropertiesAutoConfigurationMetadata
|
||||
implements AutoConfigurationMetadata {
|
||||
|
||||
private final Properties properties;
|
||||
|
||||
PropertiesAutoConfigurationMetadata(Properties properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasProcessed(String className) {
|
||||
return this.properties.containsKey(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInteger(String className, String key) {
|
||||
return getInteger(className, key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getInteger(String className, String key, Integer defaultValue) {
|
||||
String value = get(className, key);
|
||||
return (value != null ? Integer.valueOf(value) : defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSet(String className, String key) {
|
||||
return getSet(className, key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSet(String className, String key,
|
||||
Set<String> defaultValue) {
|
||||
String value = get(className, key);
|
||||
return (value != null ? StringUtils.commaDelimitedListToSet(value)
|
||||
: defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String className, String key) {
|
||||
return get(className, key, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String className, String key, String defaultValue) {
|
||||
String value = this.properties.getProperty(className + "." + key);
|
||||
return (value != null ? value : defaultValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Indicates that the package containing the annotated class should be registered with
|
||||
* {@link AutoConfigurationPackages}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
* @see AutoConfigurationPackages
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(AutoConfigurationPackages.Registrar.class)
|
||||
public @interface AutoConfigurationPackage {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConstructorArgumentValues;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.boot.context.annotation.DeterminableImports;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Class for storing auto-configuration packages for reference later (e.g. by JPA entity
|
||||
* scanner).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AutoConfigurationPackages {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AutoConfigurationPackages.class);
|
||||
|
||||
private static final String BEAN = AutoConfigurationPackages.class.getName();
|
||||
|
||||
/**
|
||||
* Determine if the auto-configuration base packages for the given bean factory are
|
||||
* available.
|
||||
* @param beanFactory the source bean factory
|
||||
* @return true if there are auto-config packages available
|
||||
*/
|
||||
public static boolean has(BeanFactory beanFactory) {
|
||||
return beanFactory.containsBean(BEAN) && !get(beanFactory).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the auto-configuration base packages for the given bean factory.
|
||||
* @param beanFactory the source bean factory
|
||||
* @return a list of auto-configuration packages
|
||||
* @throws IllegalStateException if auto-configuration is not enabled
|
||||
*/
|
||||
public static List<String> get(BeanFactory beanFactory) {
|
||||
try {
|
||||
return beanFactory.getBean(BEAN, BasePackages.class).get();
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to retrieve @EnableAutoConfiguration base packages");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically registers the auto-configuration package names. Subsequent
|
||||
* invocations will add the given package names to those that have already been
|
||||
* registered. You can use this method to manually define the base packages that will
|
||||
* be used for a given {@link BeanDefinitionRegistry}. Generally it's recommended that
|
||||
* you don't call this method directly, but instead rely on the default convention
|
||||
* where the package name is set from your {@code @EnableAutoConfiguration}
|
||||
* configuration class or classes.
|
||||
* @param registry the bean definition registry
|
||||
* @param packageNames the package names to set
|
||||
*/
|
||||
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
|
||||
if (registry.containsBeanDefinition(BEAN)) {
|
||||
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
|
||||
ConstructorArgumentValues constructorArguments = beanDefinition
|
||||
.getConstructorArgumentValues();
|
||||
constructorArguments.addIndexedArgumentValue(0,
|
||||
addBasePackages(constructorArguments, packageNames));
|
||||
}
|
||||
else {
|
||||
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
|
||||
beanDefinition.setBeanClass(BasePackages.class);
|
||||
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
|
||||
packageNames);
|
||||
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(BEAN, beanDefinition);
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] addBasePackages(
|
||||
ConstructorArgumentValues constructorArguments, String[] packageNames) {
|
||||
String[] existing = (String[]) constructorArguments
|
||||
.getIndexedArgumentValue(0, String[].class).getValue();
|
||||
Set<String> merged = new LinkedHashSet<>();
|
||||
merged.addAll(Arrays.asList(existing));
|
||||
merged.addAll(Arrays.asList(packageNames));
|
||||
return merged.toArray(new String[merged.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
|
||||
* configuration.
|
||||
*/
|
||||
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata metadata,
|
||||
BeanDefinitionRegistry registry) {
|
||||
register(registry, new PackageImport(metadata).getPackageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> determineImports(AnnotationMetadata metadata) {
|
||||
return Collections.<Object>singleton(new PackageImport(metadata));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for a package import.
|
||||
*/
|
||||
private final static class PackageImport {
|
||||
|
||||
private final String packageName;
|
||||
|
||||
PackageImport(AnnotationMetadata metadata) {
|
||||
this.packageName = ClassUtils.getPackageName(metadata.getClassName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.packageName.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
return this.packageName.equals(((PackageImport) obj).packageName);
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return this.packageName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Package Import " + this.packageName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Holder for the base package (name may be null to indicate no scanning).
|
||||
*/
|
||||
static final class BasePackages {
|
||||
|
||||
private final List<String> packages;
|
||||
|
||||
private boolean loggedBasePackageInfo;
|
||||
|
||||
BasePackages(String... names) {
|
||||
List<String> packages = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
if (StringUtils.hasText(name)) {
|
||||
packages.add(name);
|
||||
}
|
||||
}
|
||||
this.packages = packages;
|
||||
}
|
||||
|
||||
public List<String> get() {
|
||||
if (!this.loggedBasePackageInfo) {
|
||||
if (this.packages.isEmpty()) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("@EnableAutoConfiguration was declared on a class "
|
||||
+ "in the default package. Automatic @Repository and "
|
||||
+ "@Entity scanning is not enabled.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
String packageNames = StringUtils
|
||||
.collectionToCommaDelimitedString(this.packages);
|
||||
logger.debug("@EnableAutoConfiguration was declared on a class "
|
||||
+ "in the package '" + packageNames
|
||||
+ "'. Automatic @Repository and @Entity scanning is "
|
||||
+ "enabled.");
|
||||
}
|
||||
}
|
||||
this.loggedBasePackageInfo = true;
|
||||
}
|
||||
return this.packages;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Sort {@link EnableAutoConfiguration auto-configuration} classes into priority order by
|
||||
* reading {@link AutoConfigureOrder}, {@link AutoConfigureBefore} and
|
||||
* {@link AutoConfigureAfter} annotations (without loading classes).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AutoConfigurationSorter {
|
||||
|
||||
private final MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private final AutoConfigurationMetadata autoConfigurationMetadata;
|
||||
|
||||
AutoConfigurationSorter(MetadataReaderFactory metadataReaderFactory,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
Assert.notNull(metadataReaderFactory, "MetadataReaderFactory must not be null");
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.autoConfigurationMetadata = autoConfigurationMetadata;
|
||||
}
|
||||
|
||||
public List<String> getInPriorityOrder(Collection<String> classNames) {
|
||||
final AutoConfigurationClasses classes = new AutoConfigurationClasses(
|
||||
this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);
|
||||
List<String> orderedClassNames = new ArrayList<>(classNames);
|
||||
// Initially sort alphabetically
|
||||
Collections.sort(orderedClassNames);
|
||||
// Then sort by order
|
||||
orderedClassNames.sort((o1, o2) -> {
|
||||
int i1 = classes.get(o1).getOrder();
|
||||
int i2 = classes.get(o2).getOrder();
|
||||
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
|
||||
});
|
||||
// Then respect @AutoConfigureBefore @AutoConfigureAfter
|
||||
orderedClassNames = sortByAnnotation(classes, orderedClassNames);
|
||||
return orderedClassNames;
|
||||
}
|
||||
|
||||
private List<String> sortByAnnotation(AutoConfigurationClasses classes,
|
||||
List<String> classNames) {
|
||||
List<String> toSort = new ArrayList<>(classNames);
|
||||
Set<String> sorted = new LinkedHashSet<>();
|
||||
Set<String> processing = new LinkedHashSet<>();
|
||||
while (!toSort.isEmpty()) {
|
||||
doSortByAfterAnnotation(classes, toSort, sorted, processing, null);
|
||||
}
|
||||
return new ArrayList<>(sorted);
|
||||
}
|
||||
|
||||
private void doSortByAfterAnnotation(AutoConfigurationClasses classes,
|
||||
List<String> toSort, Set<String> sorted, Set<String> processing,
|
||||
String current) {
|
||||
if (current == null) {
|
||||
current = toSort.remove(0);
|
||||
}
|
||||
processing.add(current);
|
||||
for (String after : classes.getClassesRequestedAfter(current)) {
|
||||
Assert.state(!processing.contains(after),
|
||||
"AutoConfigure cycle detected between " + current + " and " + after);
|
||||
if (!sorted.contains(after) && toSort.contains(after)) {
|
||||
doSortByAfterAnnotation(classes, toSort, sorted, processing, after);
|
||||
}
|
||||
}
|
||||
processing.remove(current);
|
||||
sorted.add(current);
|
||||
}
|
||||
|
||||
private static class AutoConfigurationClasses {
|
||||
|
||||
private final Map<String, AutoConfigurationClass> classes = new HashMap<>();
|
||||
|
||||
AutoConfigurationClasses(MetadataReaderFactory metadataReaderFactory,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata,
|
||||
Collection<String> classNames) {
|
||||
for (String className : classNames) {
|
||||
this.classes.put(className, new AutoConfigurationClass(className,
|
||||
metadataReaderFactory, autoConfigurationMetadata));
|
||||
}
|
||||
}
|
||||
|
||||
public AutoConfigurationClass get(String className) {
|
||||
return this.classes.get(className);
|
||||
}
|
||||
|
||||
public Set<String> getClassesRequestedAfter(String className) {
|
||||
Set<String> rtn = new LinkedHashSet<>();
|
||||
rtn.addAll(get(className).getAfter());
|
||||
for (Map.Entry<String, AutoConfigurationClass> entry : this.classes
|
||||
.entrySet()) {
|
||||
if (entry.getValue().getBefore().contains(className)) {
|
||||
rtn.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class AutoConfigurationClass {
|
||||
|
||||
private final String className;
|
||||
|
||||
private final MetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
private final AutoConfigurationMetadata autoConfigurationMetadata;
|
||||
|
||||
private AnnotationMetadata annotationMetadata;
|
||||
|
||||
private final Set<String> before;
|
||||
|
||||
private final Set<String> after;
|
||||
|
||||
AutoConfigurationClass(String className,
|
||||
MetadataReaderFactory metadataReaderFactory,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
this.className = className;
|
||||
this.metadataReaderFactory = metadataReaderFactory;
|
||||
this.autoConfigurationMetadata = autoConfigurationMetadata;
|
||||
this.before = readBefore();
|
||||
this.after = readAfter();
|
||||
}
|
||||
|
||||
public Set<String> getBefore() {
|
||||
return this.before;
|
||||
}
|
||||
|
||||
public Set<String> getAfter() {
|
||||
return this.after;
|
||||
}
|
||||
|
||||
private int getOrder() {
|
||||
if (wasProcessed()) {
|
||||
return this.autoConfigurationMetadata.getInteger(this.className,
|
||||
"AutoConfigureOrder", AutoConfigureOrder.DEFAULT_ORDER);
|
||||
}
|
||||
Map<String, Object> attributes = getAnnotationMetadata()
|
||||
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
|
||||
return (attributes == null ? AutoConfigureOrder.DEFAULT_ORDER
|
||||
: (Integer) attributes.get("value"));
|
||||
}
|
||||
|
||||
private Set<String> readBefore() {
|
||||
if (wasProcessed()) {
|
||||
return this.autoConfigurationMetadata.getSet(this.className,
|
||||
"AutoConfigureBefore", Collections.<String>emptySet());
|
||||
}
|
||||
return getAnnotationValue(AutoConfigureBefore.class);
|
||||
}
|
||||
|
||||
private Set<String> readAfter() {
|
||||
if (wasProcessed()) {
|
||||
return this.autoConfigurationMetadata.getSet(this.className,
|
||||
"AutoConfigureAfter", Collections.<String>emptySet());
|
||||
}
|
||||
return getAnnotationValue(AutoConfigureAfter.class);
|
||||
}
|
||||
|
||||
private boolean wasProcessed() {
|
||||
return (this.autoConfigurationMetadata != null
|
||||
&& this.autoConfigurationMetadata.wasProcessed(this.className));
|
||||
}
|
||||
|
||||
private Set<String> getAnnotationValue(Class<?> annotation) {
|
||||
Map<String, Object> attributes = getAnnotationMetadata()
|
||||
.getAnnotationAttributes(annotation.getName(), true);
|
||||
if (attributes == null) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> value = new LinkedHashSet<>();
|
||||
Collections.addAll(value, (String[]) attributes.get("value"));
|
||||
Collections.addAll(value, (String[]) attributes.get("name"));
|
||||
return value;
|
||||
}
|
||||
|
||||
private AnnotationMetadata getAnnotationMetadata() {
|
||||
if (this.annotationMetadata == null) {
|
||||
try {
|
||||
MetadataReader metadataReader = this.metadataReaderFactory
|
||||
.getMetadataReader(this.className);
|
||||
this.annotationMetadata = metadataReader.getAnnotationMetadata();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(
|
||||
"Unable to read meta-data for class " + this.className, ex);
|
||||
}
|
||||
}
|
||||
return this.annotationMetadata;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.boot.context.annotation.Configurations;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link Configurations} representing auto-configuration {@code @Configuration} classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class AutoConfigurations extends Configurations implements Ordered {
|
||||
|
||||
private static final AutoConfigurationSorter SORTER = new AutoConfigurationSorter(
|
||||
new SimpleMetadataReaderFactory(), null);
|
||||
|
||||
private static final Ordered ORDER = new AutoConfigurationImportSelector();
|
||||
|
||||
protected AutoConfigurations(Collection<Class<?>> classes) {
|
||||
super(classes);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Class<?>> sort(Collection<Class<?>> classes) {
|
||||
List<String> names = classes.stream().map(Class::getName)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
List<String> sorted = SORTER.getInPriorityOrder(names);
|
||||
return sorted.stream()
|
||||
.map((className) -> ClassUtils.resolveClassName(className, null))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return ORDER.getOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AutoConfigurations merge(Set<Class<?>> mergedClasses) {
|
||||
return new AutoConfigurations(mergedClasses);
|
||||
}
|
||||
|
||||
public static AutoConfigurations of(Class<?>... classes) {
|
||||
return new AutoConfigurations(Arrays.asList(classes));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Hint for that an {@link EnableAutoConfiguration auto-configuration} should be applied
|
||||
* after other specified auto-configuration classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE })
|
||||
public @interface AutoConfigureAfter {
|
||||
|
||||
/**
|
||||
* The auto-configure classes that should have already been applied.
|
||||
* @return the classes
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The names of the auto-configure classes that should have already been applied.
|
||||
* @return the class names
|
||||
* @since 1.2.2
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Hint for that an {@link EnableAutoConfiguration auto-configuration} should be applied
|
||||
* before other specified auto-configuration classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE })
|
||||
public @interface AutoConfigureBefore {
|
||||
|
||||
/**
|
||||
* The auto-configure classes that should have not yet been applied.
|
||||
* @return the classes
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The names of the auto-configure classes that should have not yet been applied.
|
||||
* @return the class names
|
||||
* @since 1.2.2
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* Auto-configuration specific variant of Spring Framework's {@link Order} annotation.
|
||||
* Allows auto-configuration classes to be ordered among themselves without affecting the
|
||||
* order of configuration classes passed to
|
||||
* {@link AnnotationConfigApplicationContext#register(Class...)}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
|
||||
public @interface AutoConfigureOrder {
|
||||
|
||||
int DEFAULT_ORDER = 0;
|
||||
|
||||
/**
|
||||
* The order value. Default is {@code 0}.
|
||||
* @see Ordered#getOrder()
|
||||
* @return the order value
|
||||
*/
|
||||
int value() default DEFAULT_ORDER;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.validation.Validation;
|
||||
|
||||
import org.apache.catalina.mbeans.MBeanFactory;
|
||||
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationFailedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.boot.context.event.SpringApplicationEvent;
|
||||
import org.springframework.boot.context.logging.LoggingApplicationListener;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
|
||||
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
|
||||
|
||||
/**
|
||||
* {@link ApplicationListener} to trigger early initialization in a background thread of
|
||||
* time consuming tasks.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
|
||||
public class BackgroundPreinitializer
|
||||
implements ApplicationListener<SpringApplicationEvent> {
|
||||
|
||||
private static final AtomicBoolean preinitializationStarted = new AtomicBoolean(
|
||||
false);
|
||||
|
||||
private static final CountDownLatch preinitializationComplete = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(SpringApplicationEvent event) {
|
||||
if (event instanceof ApplicationEnvironmentPreparedEvent) {
|
||||
if (preinitializationStarted.compareAndSet(false, true)) {
|
||||
performPreinitialization();
|
||||
}
|
||||
}
|
||||
if ((event instanceof ApplicationReadyEvent
|
||||
|| event instanceof ApplicationFailedEvent)
|
||||
&& preinitializationStarted.get()) {
|
||||
try {
|
||||
preinitializationComplete.await();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void performPreinitialization() {
|
||||
try {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
runSafely(new MessageConverterInitializer());
|
||||
runSafely(new MBeanFactoryInitializer());
|
||||
runSafely(new ValidationInitializer());
|
||||
runSafely(new JacksonInitializer());
|
||||
runSafely(new ConversionServiceInitializer());
|
||||
preinitializationComplete.countDown();
|
||||
}
|
||||
|
||||
public void runSafely(Runnable runnable) {
|
||||
try {
|
||||
runnable.run();
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
}, "background-preinit");
|
||||
thread.start();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// This will fail on GAE where creating threads is prohibited. We can safely
|
||||
// continue but startup will be slightly slower as the initialization will now
|
||||
// happen on the main thread.
|
||||
preinitializationComplete.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Early initializer for Spring MessageConverters.
|
||||
*/
|
||||
private static class MessageConverterInitializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
new AllEncompassingFormHttpMessageConverter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Early initializer to load Tomcat MBean XML.
|
||||
*/
|
||||
private static class MBeanFactoryInitializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
new MBeanFactory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Early initializer for javax.validation.
|
||||
*/
|
||||
private static class ValidationInitializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Validation.byDefaultProvider().configure();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Early initializer for Jackson.
|
||||
*/
|
||||
private static class JacksonInitializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Jackson2ObjectMapperBuilder.json().build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Early initializer for Spring's ConversionService.
|
||||
*/
|
||||
private static class ConversionServiceInitializer implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
new DefaultFormattingConversionService();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
/**
|
||||
* Supported {@link AbstractDatabaseInitializer database initializer} modes.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public enum DatabaseInitializationMode {
|
||||
|
||||
/**
|
||||
* Always initialize the database.
|
||||
*/
|
||||
ALWAYS,
|
||||
|
||||
/**
|
||||
* Only initialize an embedded database.
|
||||
*/
|
||||
EMBEDDED,
|
||||
|
||||
/**
|
||||
* Do not initialize the database.
|
||||
*/
|
||||
NEVER
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
|
||||
/**
|
||||
* Enable auto-configuration of the Spring Application Context, attempting to guess and
|
||||
* configure beans that you are likely to need. Auto-configuration classes are usually
|
||||
* applied based on your classpath and what beans you have defined. For example, if you
|
||||
* have {@code tomcat-embedded.jar} on your classpath you are likely to want a
|
||||
* {@link TomcatServletWebServerFactory} (unless you have defined your own
|
||||
* {@link ServletWebServerFactory} bean).
|
||||
* <p>
|
||||
* When using {@link SpringBootApplication}, the auto-configuration of the context is
|
||||
* automatically enabled and adding this annotation has therefore no additional effect.
|
||||
* <p>
|
||||
* Auto-configuration tries to be as intelligent as possible and will back-away as you
|
||||
* define more of your own configuration. You can always manually {@link #exclude()} any
|
||||
* configuration that you never want to apply (use {@link #excludeName()} if you don't
|
||||
* have access to them). You can also exclude them via the
|
||||
* {@code spring.autoconfigure.exclude} property. Auto-configuration is always applied
|
||||
* after user-defined beans have been registered.
|
||||
* <p>
|
||||
* The package of the class that is annotated with {@code @EnableAutoConfiguration},
|
||||
* usually via {@code @SpringBootApplication}, has specific significance and is often used
|
||||
* as a 'default'. For example, it will be used when scanning for {@code @Entity} classes.
|
||||
* It is generally recommended that you place {@code @EnableAutoConfiguration} (if you're
|
||||
* not using {@code @SpringBootApplication}) in a root package so that all sub-packages
|
||||
* and classes can be searched.
|
||||
* <p>
|
||||
* Auto-configuration classes are regular Spring {@link Configuration} beans. They are
|
||||
* located using the {@link SpringFactoriesLoader} mechanism (keyed against this class).
|
||||
* Generally auto-configuration beans are {@link Conditional @Conditional} beans (most
|
||||
* often using {@link ConditionalOnClass @ConditionalOnClass} and
|
||||
* {@link ConditionalOnMissingBean @ConditionalOnMissingBean} annotations).
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @see ConditionalOnBean
|
||||
* @see ConditionalOnMissingBean
|
||||
* @see ConditionalOnClass
|
||||
* @see AutoConfigureAfter
|
||||
* @see SpringBootApplication
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@AutoConfigurationPackage
|
||||
@Import(AutoConfigurationImportSelector.class)
|
||||
public @interface EnableAutoConfiguration {
|
||||
|
||||
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
|
||||
|
||||
/**
|
||||
* Exclude specific auto-configuration classes such that they will never be applied.
|
||||
* @return the classes to exclude
|
||||
*/
|
||||
Class<?>[] exclude() default {};
|
||||
|
||||
/**
|
||||
* Exclude specific auto-configuration class names such that they will never be
|
||||
* applied.
|
||||
* @return the class names to exclude
|
||||
* @since 1.3.0
|
||||
*/
|
||||
String[] excludeName() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Import and apply the specified auto-configuration classes. Applies the same ordering
|
||||
* rules as {@code @EnableAutoConfiguration} but restricts the auto-configuration classes
|
||||
* to the specified set, rather than consulting {@code spring.factories}.
|
||||
* <p>
|
||||
* Can also be used to {@link #exclude()} specific auto-configuration classes such that
|
||||
* they will never be applied.
|
||||
* <p>
|
||||
* Generally, {@code @EnableAutoConfiguration} should be used in preference to this
|
||||
* annotation, however, {@code @ImportAutoConfiguration} can be useful in some situations
|
||||
* and especially when writing tests.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@Import(ImportAutoConfigurationImportSelector.class)
|
||||
public @interface ImportAutoConfiguration {
|
||||
|
||||
/**
|
||||
* The auto-configuration classes that should be imported. This is an alias for
|
||||
* {@link #classes()}.
|
||||
* @return the classes to import
|
||||
*/
|
||||
@AliasFor("classes")
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The auto-configuration classes that should be imported. When empty, the classes are
|
||||
* specified using an entry in {@code META-INF/spring.factories} where the key is the
|
||||
* fully-qualified name of the annotated class.
|
||||
* @return the classes to import
|
||||
*/
|
||||
@AliasFor("value")
|
||||
Class<?>[] classes() default {};
|
||||
|
||||
/**
|
||||
* Exclude specific auto-configuration classes such that they will never be applied.
|
||||
* @return the classes to exclude
|
||||
*/
|
||||
Class<?>[] exclude() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.context.annotation.DeterminableImports;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Variant of {@link AutoConfigurationImportSelector} for {@link ImportAutoConfiguration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class ImportAutoConfigurationImportSelector extends AutoConfigurationImportSelector
|
||||
implements DeterminableImports {
|
||||
|
||||
private static final Set<String> ANNOTATION_NAMES;
|
||||
|
||||
static {
|
||||
Set<String> names = new LinkedHashSet<>();
|
||||
names.add(ImportAutoConfiguration.class.getName());
|
||||
names.add("org.springframework.boot.autoconfigure.test.ImportAutoConfiguration");
|
||||
ANNOTATION_NAMES = Collections.unmodifiableSet(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Object> determineImports(AnnotationMetadata metadata) {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
result.addAll(getCandidateConfigurations(metadata, null));
|
||||
result.removeAll(getExclusions(metadata, null));
|
||||
return Collections.<Object>unmodifiableSet(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
|
||||
AnnotationAttributes attributes) {
|
||||
List<String> candidates = new ArrayList<>();
|
||||
Map<Class<?>, List<Annotation>> annotations = getAnnotations(metadata);
|
||||
for (Map.Entry<Class<?>, List<Annotation>> entry : annotations.entrySet()) {
|
||||
collectCandidateConfigurations(entry.getKey(), entry.getValue(), candidates);
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void collectCandidateConfigurations(Class<?> source,
|
||||
List<Annotation> annotations, List<String> candidates) {
|
||||
for (Annotation annotation : annotations) {
|
||||
candidates.addAll(getConfigurationsForAnnotation(source, annotation));
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<String> getConfigurationsForAnnotation(Class<?> source,
|
||||
Annotation annotation) {
|
||||
String[] classes = (String[]) AnnotationUtils
|
||||
.getAnnotationAttributes(annotation, true).get("classes");
|
||||
if (classes.length > 0) {
|
||||
return Arrays.asList(classes);
|
||||
}
|
||||
return loadFactoryNames(source);
|
||||
}
|
||||
|
||||
protected Collection<String> loadFactoryNames(Class<?> source) {
|
||||
return SpringFactoriesLoader.loadFactoryNames(source,
|
||||
getClass().getClassLoader());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<String> getExclusions(AnnotationMetadata metadata,
|
||||
AnnotationAttributes attributes) {
|
||||
Set<String> exclusions = new LinkedHashSet<>();
|
||||
Class<?> source = ClassUtils.resolveClassName(metadata.getClassName(), null);
|
||||
for (String annotationName : ANNOTATION_NAMES) {
|
||||
AnnotationAttributes merged = AnnotatedElementUtils
|
||||
.getMergedAnnotationAttributes(source, annotationName);
|
||||
Class<?>[] exclude = (merged == null ? null
|
||||
: merged.getClassArray("exclude"));
|
||||
if (exclude != null) {
|
||||
for (Class<?> excludeClass : exclude) {
|
||||
exclusions.add(excludeClass.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (List<Annotation> annotations : getAnnotations(metadata).values()) {
|
||||
for (Annotation annotation : annotations) {
|
||||
String[] exclude = (String[]) AnnotationUtils
|
||||
.getAnnotationAttributes(annotation, true).get("exclude");
|
||||
if (!ObjectUtils.isEmpty(exclude)) {
|
||||
exclusions.addAll(Arrays.asList(exclude));
|
||||
}
|
||||
}
|
||||
}
|
||||
return exclusions;
|
||||
}
|
||||
|
||||
protected final Map<Class<?>, List<Annotation>> getAnnotations(
|
||||
AnnotationMetadata metadata) {
|
||||
MultiValueMap<Class<?>, Annotation> annotations = new LinkedMultiValueMap<>();
|
||||
Class<?> source = ClassUtils.resolveClassName(metadata.getClassName(), null);
|
||||
collectAnnotations(source, annotations, new HashSet<>());
|
||||
return Collections.unmodifiableMap(annotations);
|
||||
}
|
||||
|
||||
private void collectAnnotations(Class<?> source,
|
||||
MultiValueMap<Class<?>, Annotation> annotations, HashSet<Class<?>> seen) {
|
||||
if (source != null && seen.add(source)) {
|
||||
for (Annotation annotation : source.getDeclaredAnnotations()) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||
if (ANNOTATION_NAMES
|
||||
.contains(annotation.annotationType().getName())) {
|
||||
annotations.add(source, annotation);
|
||||
}
|
||||
collectAnnotations(annotation.annotationType(), annotations, seen);
|
||||
}
|
||||
}
|
||||
collectAnnotations(source.getSuperclass(), annotations, seen);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return super.getOrder() - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleInvalidExcludes(List<String> invalidExcludes) {
|
||||
// Ignore for test
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.AnnotationConfigUtils;
|
||||
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.PriorityOrdered;
|
||||
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
|
||||
/**
|
||||
* {@link ApplicationContextInitializer} to create a shared
|
||||
* {@link CachingMetadataReaderFactory} between the
|
||||
* {@link ConfigurationClassPostProcessor} and Spring Boot.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.0
|
||||
*/
|
||||
class SharedMetadataReaderFactoryContextInitializer
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
|
||||
|
||||
public static final String BEAN_NAME = "org.springframework.boot.autoconfigure."
|
||||
+ "internalCachingMetadataReaderFactory";
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
applicationContext.addBeanFactoryPostProcessor(
|
||||
new CachingMetadataReaderFactoryPostProcessor());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanDefinitionRegistryPostProcessor} to register the
|
||||
* {@link CachingMetadataReaderFactory} and configure the
|
||||
* {@link ConfigurationClassPostProcessor}.
|
||||
*/
|
||||
private static class CachingMetadataReaderFactoryPostProcessor
|
||||
implements BeanDefinitionRegistryPostProcessor, PriorityOrdered {
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
// Must happen before the ConfigurationClassPostProcessor is created
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
|
||||
throws BeansException {
|
||||
register(registry);
|
||||
configureConfigurationClassPostProcessor(registry);
|
||||
}
|
||||
|
||||
private void register(BeanDefinitionRegistry registry) {
|
||||
RootBeanDefinition definition = new RootBeanDefinition(
|
||||
SharedMetadataReaderFactoryBean.class);
|
||||
registry.registerBeanDefinition(BEAN_NAME, definition);
|
||||
}
|
||||
|
||||
private void configureConfigurationClassPostProcessor(
|
||||
BeanDefinitionRegistry registry) {
|
||||
try {
|
||||
BeanDefinition definition = registry.getBeanDefinition(
|
||||
AnnotationConfigUtils.CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME);
|
||||
definition.getPropertyValues().add("metadataReaderFactory",
|
||||
new RuntimeBeanReference(BEAN_NAME));
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link FactoryBean} to create the shared {@link MetadataReaderFactory}.
|
||||
*/
|
||||
static class SharedMetadataReaderFactoryBean
|
||||
implements FactoryBean<ConcurrentReferenceCachingMetadataReaderFactory>,
|
||||
BeanClassLoaderAware, ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private ConcurrentReferenceCachingMetadataReaderFactory metadataReaderFactory;
|
||||
|
||||
@Override
|
||||
public void setBeanClassLoader(ClassLoader classLoader) {
|
||||
this.metadataReaderFactory = new ConcurrentReferenceCachingMetadataReaderFactory(
|
||||
classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConcurrentReferenceCachingMetadataReaderFactory getObject()
|
||||
throws Exception {
|
||||
return this.metadataReaderFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return CachingMetadataReaderFactory.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
||||
this.metadataReaderFactory.clearCache();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.context.TypeExcludeFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.core.annotation.AliasFor;
|
||||
|
||||
/**
|
||||
* Indicates a {@link Configuration configuration} class that declares one or more
|
||||
* {@link Bean @Bean} methods and also triggers {@link EnableAutoConfiguration
|
||||
* auto-configuration} and {@link ComponentScan component scanning}. This is a convenience
|
||||
* annotation that is equivalent to declaring {@code @Configuration},
|
||||
* {@code @EnableAutoConfiguration} and {@code @ComponentScan}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Inherited
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@ComponentScan(excludeFilters = {
|
||||
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
|
||||
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
|
||||
public @interface SpringBootApplication {
|
||||
|
||||
/**
|
||||
* Exclude specific auto-configuration classes such that they will never be applied.
|
||||
* @return the classes to exclude
|
||||
*/
|
||||
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "exclude")
|
||||
Class<?>[] exclude() default {};
|
||||
|
||||
/**
|
||||
* Exclude specific auto-configuration class names such that they will never be
|
||||
* applied.
|
||||
* @return the class names to exclude
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@AliasFor(annotation = EnableAutoConfiguration.class, attribute = "excludeName")
|
||||
String[] excludeName() default {};
|
||||
|
||||
/**
|
||||
* Base packages to scan for annotated components. Use {@link #scanBasePackageClasses}
|
||||
* for a type-safe alternative to String-based package names.
|
||||
* @return base packages to scan
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
|
||||
String[] scanBasePackages() default {};
|
||||
|
||||
/**
|
||||
* Type-safe alternative to {@link #scanBasePackages} for specifying the packages to
|
||||
* scan for annotated components. The package of each class specified will be scanned.
|
||||
* <p>
|
||||
* Consider creating a special no-op marker class or interface in each package that
|
||||
* serves no purpose other than being referenced by this attribute.
|
||||
* @return base packages to scan
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
|
||||
Class<?>[] scanBasePackageClasses() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.admin;
|
||||
|
||||
import javax.management.MalformedObjectNameException;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.admin.SpringApplicationAdminMXBean;
|
||||
import org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.jmx.export.MBeanExporter;
|
||||
|
||||
/**
|
||||
* Register a JMX component that allows to administer the current application. Intended
|
||||
* for internal use only.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.3.0
|
||||
* @see SpringApplicationAdminMXBean
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter(JmxAutoConfiguration.class)
|
||||
@ConditionalOnProperty(prefix = "spring.application.admin", value = "enabled", havingValue = "true", matchIfMissing = false)
|
||||
public class SpringApplicationAdminJmxAutoConfiguration {
|
||||
|
||||
/**
|
||||
* The property to use to customize the {@code ObjectName} of the application admin
|
||||
* mbean.
|
||||
*/
|
||||
private static final String JMX_NAME_PROPERTY = "spring.application.admin.jmx-name";
|
||||
|
||||
/**
|
||||
* The default {@code ObjectName} of the application admin mbean.
|
||||
*/
|
||||
private static final String DEFAULT_JMX_NAME = "org.springframework.boot:type=Admin,name=SpringApplication";
|
||||
|
||||
private final MBeanExporter mbeanExporter;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
public SpringApplicationAdminJmxAutoConfiguration(
|
||||
ObjectProvider<MBeanExporter> mbeanExporter, Environment environment) {
|
||||
this.mbeanExporter = mbeanExporter.getIfAvailable();
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SpringApplicationAdminMXBeanRegistrar springApplicationAdminRegistrar()
|
||||
throws MalformedObjectNameException {
|
||||
String jmxName = this.environment.getProperty(JMX_NAME_PROPERTY,
|
||||
DEFAULT_JMX_NAME);
|
||||
if (this.mbeanExporter != null) { // Make sure to not register that MBean twice
|
||||
this.mbeanExporter.addExcludedBean(jmxName);
|
||||
}
|
||||
return new SpringApplicationAdminMXBeanRegistrar(jmxName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for admin-related features.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.admin;
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.amqp;
|
||||
|
||||
import org.springframework.amqp.rabbit.config.AbstractRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.config.RetryInterceptorBuilder;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
|
||||
import org.springframework.amqp.rabbit.retry.RejectAndDontRequeueRecoverer;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.boot.autoconfigure.amqp.RabbitProperties.ListenerRetry;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Configure {@link RabbitListenerContainerFactory} with sensible defaults.
|
||||
*
|
||||
* @param <T> the container factory type.
|
||||
* @author Gary Russell
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public abstract class AbstractRabbitListenerContainerFactoryConfigurer<T extends AbstractRabbitListenerContainerFactory<?>> {
|
||||
|
||||
private MessageConverter messageConverter;
|
||||
|
||||
private MessageRecoverer messageRecoverer;
|
||||
|
||||
private RabbitProperties rabbitProperties;
|
||||
|
||||
/**
|
||||
* Set the {@link MessageConverter} to use or {@code null} if the out-of-the-box
|
||||
* converter should be used.
|
||||
* @param messageConverter the {@link MessageConverter}
|
||||
*/
|
||||
protected void setMessageConverter(MessageConverter messageConverter) {
|
||||
this.messageConverter = messageConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link MessageRecoverer} to use or {@code null} to rely on the default.
|
||||
* @param messageRecoverer the {@link MessageRecoverer}
|
||||
*/
|
||||
protected void setMessageRecoverer(MessageRecoverer messageRecoverer) {
|
||||
this.messageRecoverer = messageRecoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link RabbitProperties} to use.
|
||||
* @param rabbitProperties the {@link RabbitProperties}
|
||||
*/
|
||||
protected void setRabbitProperties(RabbitProperties rabbitProperties) {
|
||||
this.rabbitProperties = rabbitProperties;
|
||||
}
|
||||
|
||||
protected final RabbitProperties getRabbitProperties() {
|
||||
return this.rabbitProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the specified rabbit listener container factory. The factory can be
|
||||
* further tuned and default settings can be overridden.
|
||||
* @param factory the {@link AbstractRabbitListenerContainerFactory} instance to
|
||||
* configure
|
||||
* @param connectionFactory the {@link ConnectionFactory} to use
|
||||
*/
|
||||
public abstract void configure(T factory, ConnectionFactory connectionFactory);
|
||||
|
||||
protected void configure(T factory, ConnectionFactory connectionFactory,
|
||||
RabbitProperties.AmqpContainer configuration) {
|
||||
Assert.notNull(factory, "Factory must not be null");
|
||||
Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
|
||||
Assert.notNull(configuration, "Configuration must not be null");
|
||||
factory.setConnectionFactory(connectionFactory);
|
||||
if (this.messageConverter != null) {
|
||||
factory.setMessageConverter(this.messageConverter);
|
||||
}
|
||||
factory.setAutoStartup(configuration.isAutoStartup());
|
||||
if (configuration.getAcknowledgeMode() != null) {
|
||||
factory.setAcknowledgeMode(configuration.getAcknowledgeMode());
|
||||
}
|
||||
if (configuration.getPrefetch() != null) {
|
||||
factory.setPrefetchCount(configuration.getPrefetch());
|
||||
}
|
||||
if (configuration.getDefaultRequeueRejected() != null) {
|
||||
factory.setDefaultRequeueRejected(configuration.getDefaultRequeueRejected());
|
||||
}
|
||||
if (configuration.getIdleEventInterval() != null) {
|
||||
factory.setIdleEventInterval(configuration.getIdleEventInterval());
|
||||
}
|
||||
ListenerRetry retryConfig = configuration.getRetry();
|
||||
if (retryConfig.isEnabled()) {
|
||||
RetryInterceptorBuilder<?> builder = (retryConfig.isStateless()
|
||||
? RetryInterceptorBuilder.stateless()
|
||||
: RetryInterceptorBuilder.stateful());
|
||||
builder.maxAttempts(retryConfig.getMaxAttempts());
|
||||
builder.backOffOptions(retryConfig.getInitialInterval(),
|
||||
retryConfig.getMultiplier(), retryConfig.getMaxInterval());
|
||||
MessageRecoverer recoverer = (this.messageRecoverer != null
|
||||
? this.messageRecoverer : new RejectAndDontRequeueRecoverer());
|
||||
builder.recoverer(recoverer);
|
||||
factory.setAdviceChain(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.amqp;
|
||||
|
||||
import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
|
||||
/**
|
||||
* Configure {@link DirectRabbitListenerContainerFactoryConfigurer} with sensible
|
||||
* defaults.
|
||||
*
|
||||
* @author Gary Russell
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.0
|
||||
*/
|
||||
public final class DirectRabbitListenerContainerFactoryConfigurer extends
|
||||
AbstractRabbitListenerContainerFactoryConfigurer<DirectRabbitListenerContainerFactory> {
|
||||
|
||||
@Override
|
||||
public void configure(DirectRabbitListenerContainerFactory factory,
|
||||
ConnectionFactory connectionFactory) {
|
||||
RabbitProperties.DirectContainer config = getRabbitProperties().getListener()
|
||||
.getDirect();
|
||||
configure(factory, connectionFactory, config);
|
||||
if (config.getConsumersPerQueue() != null) {
|
||||
factory.setConsumersPerQueue(config.getConsumersPerQueue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.amqp;
|
||||
|
||||
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
|
||||
import org.springframework.amqp.rabbit.config.DirectRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
|
||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration for Spring AMQP annotation driven endpoints.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Josh Thornhill
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(EnableRabbit.class)
|
||||
class RabbitAnnotationDrivenConfiguration {
|
||||
|
||||
private final ObjectProvider<MessageConverter> messageConverter;
|
||||
|
||||
private final ObjectProvider<MessageRecoverer> messageRecoverer;
|
||||
|
||||
private final RabbitProperties properties;
|
||||
|
||||
RabbitAnnotationDrivenConfiguration(ObjectProvider<MessageConverter> messageConverter,
|
||||
ObjectProvider<MessageRecoverer> messageRecoverer,
|
||||
RabbitProperties properties) {
|
||||
this.messageConverter = messageConverter;
|
||||
this.messageRecoverer = messageRecoverer;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SimpleRabbitListenerContainerFactoryConfigurer simpleRabbitListenerContainerFactoryConfigurer() {
|
||||
SimpleRabbitListenerContainerFactoryConfigurer configurer = new SimpleRabbitListenerContainerFactoryConfigurer();
|
||||
configurer.setMessageConverter(this.messageConverter.getIfUnique());
|
||||
configurer.setMessageRecoverer(this.messageRecoverer.getIfUnique());
|
||||
configurer.setRabbitProperties(this.properties);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
@Bean(name = "rabbitListenerContainerFactory")
|
||||
@ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
|
||||
@ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "simple", matchIfMissing = true)
|
||||
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
|
||||
SimpleRabbitListenerContainerFactoryConfigurer configurer,
|
||||
ConnectionFactory connectionFactory) {
|
||||
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
|
||||
configurer.configure(factory, connectionFactory);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public DirectRabbitListenerContainerFactoryConfigurer directRabbitListenerContainerFactoryConfigurer() {
|
||||
DirectRabbitListenerContainerFactoryConfigurer configurer = new DirectRabbitListenerContainerFactoryConfigurer();
|
||||
configurer.setMessageConverter(this.messageConverter.getIfUnique());
|
||||
configurer.setMessageRecoverer(this.messageRecoverer.getIfUnique());
|
||||
configurer.setRabbitProperties(this.properties);
|
||||
return configurer;
|
||||
}
|
||||
|
||||
@Bean(name = "rabbitListenerContainerFactory")
|
||||
@ConditionalOnMissingBean(name = "rabbitListenerContainerFactory")
|
||||
@ConditionalOnProperty(prefix = "spring.rabbitmq.listener", name = "type", havingValue = "direct")
|
||||
public DirectRabbitListenerContainerFactory directRabbitListenerContainerFactory(
|
||||
DirectRabbitListenerContainerFactoryConfigurer configurer,
|
||||
ConnectionFactory connectionFactory) {
|
||||
DirectRabbitListenerContainerFactory factory = new DirectRabbitListenerContainerFactory();
|
||||
configurer.configure(factory, connectionFactory);
|
||||
return factory;
|
||||
}
|
||||
|
||||
@EnableRabbit
|
||||
@ConditionalOnMissingBean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
|
||||
protected static class EnableRabbitConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.amqp;
|
||||
|
||||
import com.rabbitmq.client.Channel;
|
||||
|
||||
import org.springframework.amqp.core.AmqpAdmin;
|
||||
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean;
|
||||
import org.springframework.amqp.rabbit.core.RabbitAdmin;
|
||||
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link RabbitTemplate}.
|
||||
* <p>
|
||||
* This configuration class is active only when the RabbitMQ and Spring AMQP client
|
||||
* libraries are on the classpath.
|
||||
* <P>
|
||||
* Registers the following beans:
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.amqp.rabbit.core.RabbitTemplate RabbitTemplate} if there
|
||||
* is no other bean of the same type in the context.</li>
|
||||
* <li>{@link org.springframework.amqp.rabbit.connection.CachingConnectionFactory
|
||||
* CachingConnectionFactory} instance if there is no other bean of the same type in the
|
||||
* context.</li>
|
||||
* <li>{@link org.springframework.amqp.core.AmqpAdmin } instance as long as
|
||||
* {@literal spring.rabbitmq.dynamic=true}.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* The {@link org.springframework.amqp.rabbit.connection.CachingConnectionFactory} honors
|
||||
* the following properties:
|
||||
* <ul>
|
||||
* <li>{@literal spring.rabbitmq.port} is used to specify the port to which the client
|
||||
* should connect, and defaults to 5672.</li>
|
||||
* <li>{@literal spring.rabbitmq.username} is used to specify the (optional) username.
|
||||
* </li>
|
||||
* <li>{@literal spring.rabbitmq.password} is used to specify the (optional) password.
|
||||
* </li>
|
||||
* <li>{@literal spring.rabbitmq.host} is used to specify the host, and defaults to
|
||||
* {@literal localhost}.</li>
|
||||
* <li>{@literal spring.rabbitmq.virtualHost} is used to specify the (optional) virtual
|
||||
* host to which the client should connect.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Josh Long
|
||||
* @author Stephane Nicoll
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
|
||||
@EnableConfigurationProperties(RabbitProperties.class)
|
||||
@Import(RabbitAnnotationDrivenConfiguration.class)
|
||||
public class RabbitAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(ConnectionFactory.class)
|
||||
protected static class RabbitConnectionFactoryCreator {
|
||||
|
||||
@Bean
|
||||
public CachingConnectionFactory rabbitConnectionFactory(RabbitProperties config)
|
||||
throws Exception {
|
||||
RabbitConnectionFactoryBean factory = new RabbitConnectionFactoryBean();
|
||||
if (config.determineHost() != null) {
|
||||
factory.setHost(config.determineHost());
|
||||
}
|
||||
factory.setPort(config.determinePort());
|
||||
if (config.determineUsername() != null) {
|
||||
factory.setUsername(config.determineUsername());
|
||||
}
|
||||
if (config.determinePassword() != null) {
|
||||
factory.setPassword(config.determinePassword());
|
||||
}
|
||||
if (config.determineVirtualHost() != null) {
|
||||
factory.setVirtualHost(config.determineVirtualHost());
|
||||
}
|
||||
if (config.getRequestedHeartbeat() != null) {
|
||||
factory.setRequestedHeartbeat(config.getRequestedHeartbeat());
|
||||
}
|
||||
RabbitProperties.Ssl ssl = config.getSsl();
|
||||
if (ssl.isEnabled()) {
|
||||
factory.setUseSSL(true);
|
||||
if (ssl.getAlgorithm() != null) {
|
||||
factory.setSslAlgorithm(ssl.getAlgorithm());
|
||||
}
|
||||
factory.setKeyStoreType(ssl.getKeyStoreType());
|
||||
factory.setKeyStore(ssl.getKeyStore());
|
||||
factory.setKeyStorePassphrase(ssl.getKeyStorePassword());
|
||||
factory.setTrustStoreType(ssl.getTrustStoreType());
|
||||
factory.setTrustStore(ssl.getTrustStore());
|
||||
factory.setTrustStorePassphrase(ssl.getTrustStorePassword());
|
||||
}
|
||||
if (config.getConnectionTimeout() != null) {
|
||||
factory.setConnectionTimeout(config.getConnectionTimeout());
|
||||
}
|
||||
factory.afterPropertiesSet();
|
||||
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(
|
||||
factory.getObject());
|
||||
connectionFactory.setAddresses(config.determineAddresses());
|
||||
connectionFactory.setPublisherConfirms(config.isPublisherConfirms());
|
||||
connectionFactory.setPublisherReturns(config.isPublisherReturns());
|
||||
if (config.getCache().getChannel().getSize() != null) {
|
||||
connectionFactory
|
||||
.setChannelCacheSize(config.getCache().getChannel().getSize());
|
||||
}
|
||||
if (config.getCache().getConnection().getMode() != null) {
|
||||
connectionFactory
|
||||
.setCacheMode(config.getCache().getConnection().getMode());
|
||||
}
|
||||
if (config.getCache().getConnection().getSize() != null) {
|
||||
connectionFactory.setConnectionCacheSize(
|
||||
config.getCache().getConnection().getSize());
|
||||
}
|
||||
if (config.getCache().getChannel().getCheckoutTimeout() != null) {
|
||||
connectionFactory.setChannelCheckoutTimeout(
|
||||
config.getCache().getChannel().getCheckoutTimeout());
|
||||
}
|
||||
return connectionFactory;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Import(RabbitConnectionFactoryCreator.class)
|
||||
protected static class RabbitTemplateConfiguration {
|
||||
|
||||
private final ObjectProvider<MessageConverter> messageConverter;
|
||||
|
||||
private final RabbitProperties properties;
|
||||
|
||||
public RabbitTemplateConfiguration(
|
||||
ObjectProvider<MessageConverter> messageConverter,
|
||||
RabbitProperties properties) {
|
||||
this.messageConverter = messageConverter;
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnSingleCandidate(ConnectionFactory.class)
|
||||
@ConditionalOnMissingBean(RabbitTemplate.class)
|
||||
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
|
||||
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
|
||||
MessageConverter messageConverter = this.messageConverter.getIfUnique();
|
||||
if (messageConverter != null) {
|
||||
rabbitTemplate.setMessageConverter(messageConverter);
|
||||
}
|
||||
rabbitTemplate.setMandatory(determineMandatoryFlag());
|
||||
RabbitProperties.Template templateProperties = this.properties.getTemplate();
|
||||
RabbitProperties.Retry retryProperties = templateProperties.getRetry();
|
||||
if (retryProperties.isEnabled()) {
|
||||
rabbitTemplate.setRetryTemplate(createRetryTemplate(retryProperties));
|
||||
}
|
||||
if (templateProperties.getReceiveTimeout() != null) {
|
||||
rabbitTemplate.setReceiveTimeout(templateProperties.getReceiveTimeout());
|
||||
}
|
||||
if (templateProperties.getReplyTimeout() != null) {
|
||||
rabbitTemplate.setReplyTimeout(templateProperties.getReplyTimeout());
|
||||
}
|
||||
return rabbitTemplate;
|
||||
}
|
||||
|
||||
private boolean determineMandatoryFlag() {
|
||||
Boolean mandatory = this.properties.getTemplate().getMandatory();
|
||||
return (mandatory != null ? mandatory : this.properties.isPublisherReturns());
|
||||
}
|
||||
|
||||
private RetryTemplate createRetryTemplate(RabbitProperties.Retry properties) {
|
||||
RetryTemplate template = new RetryTemplate();
|
||||
SimpleRetryPolicy policy = new SimpleRetryPolicy();
|
||||
policy.setMaxAttempts(properties.getMaxAttempts());
|
||||
template.setRetryPolicy(policy);
|
||||
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
|
||||
backOffPolicy.setInitialInterval(properties.getInitialInterval());
|
||||
backOffPolicy.setMultiplier(properties.getMultiplier());
|
||||
backOffPolicy.setMaxInterval(properties.getMaxInterval());
|
||||
template.setBackOffPolicy(backOffPolicy);
|
||||
return template;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnSingleCandidate(ConnectionFactory.class)
|
||||
@ConditionalOnProperty(prefix = "spring.rabbitmq", name = "dynamic", matchIfMissing = true)
|
||||
@ConditionalOnMissingBean(AmqpAdmin.class)
|
||||
public AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory) {
|
||||
return new RabbitAdmin(connectionFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(RabbitMessagingTemplate.class)
|
||||
@ConditionalOnMissingBean(RabbitMessagingTemplate.class)
|
||||
@Import(RabbitTemplateConfiguration.class)
|
||||
protected static class MessagingTemplateConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnSingleCandidate(RabbitTemplate.class)
|
||||
public RabbitMessagingTemplate rabbitMessagingTemplate(
|
||||
RabbitTemplate rabbitTemplate) {
|
||||
return new RabbitMessagingTemplate(rabbitTemplate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,889 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.amqp;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.amqp.core.AcknowledgeMode;
|
||||
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory.CacheMode;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Configuration properties for Rabbit.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @author Josh Thornhill
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.rabbitmq")
|
||||
public class RabbitProperties {
|
||||
|
||||
/**
|
||||
* RabbitMQ host.
|
||||
*/
|
||||
private String host = "localhost";
|
||||
|
||||
/**
|
||||
* RabbitMQ port.
|
||||
*/
|
||||
private int port = 5672;
|
||||
|
||||
/**
|
||||
* Login user to authenticate to the broker.
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* Login to authenticate against the broker.
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* SSL configuration.
|
||||
*/
|
||||
private final Ssl ssl = new Ssl();
|
||||
|
||||
/**
|
||||
* Virtual host to use when connecting to the broker.
|
||||
*/
|
||||
private String virtualHost;
|
||||
|
||||
/**
|
||||
* Comma-separated list of addresses to which the client should connect.
|
||||
*/
|
||||
private String addresses;
|
||||
|
||||
/**
|
||||
* Requested heartbeat timeout, in seconds; zero for none.
|
||||
*/
|
||||
private Integer requestedHeartbeat;
|
||||
|
||||
/**
|
||||
* Enable publisher confirms.
|
||||
*/
|
||||
private boolean publisherConfirms;
|
||||
|
||||
/**
|
||||
* Enable publisher returns.
|
||||
*/
|
||||
private boolean publisherReturns;
|
||||
|
||||
/**
|
||||
* Connection timeout, in milliseconds; zero for infinite.
|
||||
*/
|
||||
private Integer connectionTimeout;
|
||||
|
||||
/**
|
||||
* Cache configuration.
|
||||
*/
|
||||
private final Cache cache = new Cache();
|
||||
|
||||
/**
|
||||
* Listener container configuration.
|
||||
*/
|
||||
private final Listener listener = new Listener();
|
||||
|
||||
private final Template template = new Template();
|
||||
|
||||
private List<Address> parsedAddresses;
|
||||
|
||||
public String getHost() {
|
||||
return this.host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the host from the first address, or the configured host if no addresses
|
||||
* have been set.
|
||||
* @return the host
|
||||
* @see #setAddresses(String)
|
||||
* @see #getHost()
|
||||
*/
|
||||
public String determineHost() {
|
||||
if (CollectionUtils.isEmpty(this.parsedAddresses)) {
|
||||
return getHost();
|
||||
}
|
||||
return this.parsedAddresses.get(0).host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the port from the first address, or the configured port if no addresses
|
||||
* have been set.
|
||||
* @return the port
|
||||
* @see #setAddresses(String)
|
||||
* @see #getPort()
|
||||
*/
|
||||
public int determinePort() {
|
||||
if (CollectionUtils.isEmpty(this.parsedAddresses)) {
|
||||
return getPort();
|
||||
}
|
||||
Address address = this.parsedAddresses.get(0);
|
||||
return address.port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getAddresses() {
|
||||
return this.addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the comma-separated addresses or a single address ({@code host:port})
|
||||
* created from the configured host and port if no addresses have been set.
|
||||
* @return the addresses
|
||||
*/
|
||||
public String determineAddresses() {
|
||||
if (CollectionUtils.isEmpty(this.parsedAddresses)) {
|
||||
return this.host + ":" + this.port;
|
||||
}
|
||||
List<String> addressStrings = new ArrayList<>();
|
||||
for (Address parsedAddress : this.parsedAddresses) {
|
||||
addressStrings.add(parsedAddress.host + ":" + parsedAddress.port);
|
||||
}
|
||||
return StringUtils.collectionToCommaDelimitedString(addressStrings);
|
||||
}
|
||||
|
||||
public void setAddresses(String addresses) {
|
||||
this.addresses = addresses;
|
||||
this.parsedAddresses = parseAddresses(addresses);
|
||||
}
|
||||
|
||||
private List<Address> parseAddresses(String addresses) {
|
||||
List<Address> parsedAddresses = new ArrayList<>();
|
||||
for (String address : StringUtils.commaDelimitedListToStringArray(addresses)) {
|
||||
parsedAddresses.add(new Address(address));
|
||||
}
|
||||
return parsedAddresses;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
/**
|
||||
* If addresses have been set and the first address has a username it is returned.
|
||||
* Otherwise returns the result of calling {@code getUsername()}.
|
||||
* @return the username
|
||||
* @see #setAddresses(String)
|
||||
* @see #getUsername()
|
||||
*/
|
||||
public String determineUsername() {
|
||||
if (CollectionUtils.isEmpty(this.parsedAddresses)) {
|
||||
return this.username;
|
||||
}
|
||||
Address address = this.parsedAddresses.get(0);
|
||||
return address.username == null ? this.username : address.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
/**
|
||||
* If addresses have been set and the first address has a password it is returned.
|
||||
* Otherwise returns the result of calling {@code getPassword()}.
|
||||
* @return the password or {@code null}
|
||||
* @see #setAddresses(String)
|
||||
* @see #getPassword()
|
||||
*/
|
||||
public String determinePassword() {
|
||||
if (CollectionUtils.isEmpty(this.parsedAddresses)) {
|
||||
return getPassword();
|
||||
}
|
||||
Address address = this.parsedAddresses.get(0);
|
||||
return address.password == null ? getPassword() : address.password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public Ssl getSsl() {
|
||||
return this.ssl;
|
||||
}
|
||||
|
||||
public String getVirtualHost() {
|
||||
return this.virtualHost;
|
||||
}
|
||||
|
||||
/**
|
||||
* If addresses have been set and the first address has a virtual host it is returned.
|
||||
* Otherwise returns the result of calling {@code getVirtualHost()}.
|
||||
* @return the virtual host or {@code null}
|
||||
* @see #setAddresses(String)
|
||||
* @see #getVirtualHost()
|
||||
*/
|
||||
public String determineVirtualHost() {
|
||||
if (CollectionUtils.isEmpty(this.parsedAddresses)) {
|
||||
return getVirtualHost();
|
||||
}
|
||||
Address address = this.parsedAddresses.get(0);
|
||||
return address.virtualHost == null ? getVirtualHost() : address.virtualHost;
|
||||
}
|
||||
|
||||
public void setVirtualHost(String virtualHost) {
|
||||
this.virtualHost = ("".equals(virtualHost) ? "/" : virtualHost);
|
||||
}
|
||||
|
||||
public Integer getRequestedHeartbeat() {
|
||||
return this.requestedHeartbeat;
|
||||
}
|
||||
|
||||
public void setRequestedHeartbeat(Integer requestedHeartbeat) {
|
||||
this.requestedHeartbeat = requestedHeartbeat;
|
||||
}
|
||||
|
||||
public boolean isPublisherConfirms() {
|
||||
return this.publisherConfirms;
|
||||
}
|
||||
|
||||
public void setPublisherConfirms(boolean publisherConfirms) {
|
||||
this.publisherConfirms = publisherConfirms;
|
||||
}
|
||||
|
||||
public boolean isPublisherReturns() {
|
||||
return this.publisherReturns;
|
||||
}
|
||||
|
||||
public void setPublisherReturns(boolean publisherReturns) {
|
||||
this.publisherReturns = publisherReturns;
|
||||
}
|
||||
|
||||
public Integer getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(Integer connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
public Cache getCache() {
|
||||
return this.cache;
|
||||
}
|
||||
|
||||
public Listener getListener() {
|
||||
return this.listener;
|
||||
}
|
||||
|
||||
public Template getTemplate() {
|
||||
return this.template;
|
||||
}
|
||||
|
||||
public static class Ssl {
|
||||
|
||||
/**
|
||||
* Enable SSL support.
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* Path to the key store that holds the SSL certificate.
|
||||
*/
|
||||
private String keyStore;
|
||||
|
||||
/**
|
||||
* Key store type.
|
||||
*/
|
||||
private String keyStoreType = "PKCS12";
|
||||
|
||||
/**
|
||||
* Password used to access the key store.
|
||||
*/
|
||||
private String keyStorePassword;
|
||||
|
||||
/**
|
||||
* Trust store that holds SSL certificates.
|
||||
*/
|
||||
private String trustStore;
|
||||
|
||||
/**
|
||||
* Trust store type.
|
||||
*/
|
||||
private String trustStoreType = "JKS";
|
||||
|
||||
/**
|
||||
* Password used to access the trust store.
|
||||
*/
|
||||
private String trustStorePassword;
|
||||
|
||||
/**
|
||||
* SSL algorithm to use (e.g. TLSv1.1). Default is set automatically by the rabbit
|
||||
* client library.
|
||||
*/
|
||||
private String algorithm;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getKeyStore() {
|
||||
return this.keyStore;
|
||||
}
|
||||
|
||||
public void setKeyStore(String keyStore) {
|
||||
this.keyStore = keyStore;
|
||||
}
|
||||
|
||||
public String getKeyStoreType() {
|
||||
return this.keyStoreType;
|
||||
}
|
||||
|
||||
public void setKeyStoreType(String keyStoreType) {
|
||||
this.keyStoreType = keyStoreType;
|
||||
}
|
||||
|
||||
public String getKeyStorePassword() {
|
||||
return this.keyStorePassword;
|
||||
}
|
||||
|
||||
public void setKeyStorePassword(String keyStorePassword) {
|
||||
this.keyStorePassword = keyStorePassword;
|
||||
}
|
||||
|
||||
public String getTrustStore() {
|
||||
return this.trustStore;
|
||||
}
|
||||
|
||||
public void setTrustStore(String trustStore) {
|
||||
this.trustStore = trustStore;
|
||||
}
|
||||
|
||||
public String getTrustStoreType() {
|
||||
return this.trustStoreType;
|
||||
}
|
||||
|
||||
public void setTrustStoreType(String trustStoreType) {
|
||||
this.trustStoreType = trustStoreType;
|
||||
}
|
||||
|
||||
public String getTrustStorePassword() {
|
||||
return this.trustStorePassword;
|
||||
}
|
||||
|
||||
public void setTrustStorePassword(String trustStorePassword) {
|
||||
this.trustStorePassword = trustStorePassword;
|
||||
}
|
||||
|
||||
public String getAlgorithm() {
|
||||
return this.algorithm;
|
||||
}
|
||||
|
||||
public void setAlgorithm(String sslAlgorithm) {
|
||||
this.algorithm = sslAlgorithm;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Cache {
|
||||
|
||||
private final Channel channel = new Channel();
|
||||
|
||||
private final Connection connection = new Connection();
|
||||
|
||||
public Channel getChannel() {
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
public Connection getConnection() {
|
||||
return this.connection;
|
||||
}
|
||||
|
||||
public static class Channel {
|
||||
|
||||
/**
|
||||
* Number of channels to retain in the cache. When "check-timeout" > 0, max
|
||||
* channels per connection.
|
||||
*/
|
||||
private Integer size;
|
||||
|
||||
/**
|
||||
* Number of milliseconds to wait to obtain a channel if the cache size has
|
||||
* been reached. If 0, always create a new channel.
|
||||
*/
|
||||
private Long checkoutTimeout;
|
||||
|
||||
public Integer getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Long getCheckoutTimeout() {
|
||||
return this.checkoutTimeout;
|
||||
}
|
||||
|
||||
public void setCheckoutTimeout(Long checkoutTimeout) {
|
||||
this.checkoutTimeout = checkoutTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Connection {
|
||||
|
||||
/**
|
||||
* Connection factory cache mode.
|
||||
*/
|
||||
private CacheMode mode = CacheMode.CHANNEL;
|
||||
|
||||
/**
|
||||
* Number of connections to cache. Only applies when mode is CONNECTION.
|
||||
*/
|
||||
private Integer size;
|
||||
|
||||
public CacheMode getMode() {
|
||||
return this.mode;
|
||||
}
|
||||
|
||||
public void setMode(CacheMode mode) {
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
public Integer getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum ContainerType {
|
||||
|
||||
/**
|
||||
* Container where the RabbitMQ consumer dispatches messages to an invoker thread.
|
||||
*/
|
||||
SIMPLE,
|
||||
|
||||
/**
|
||||
* Container where the listener is invoked directly on the RabbitMQ consumer
|
||||
* thread.
|
||||
*/
|
||||
DIRECT
|
||||
|
||||
}
|
||||
|
||||
public static class Listener {
|
||||
|
||||
/**
|
||||
* Listener container type.
|
||||
*/
|
||||
private ContainerType type = ContainerType.SIMPLE;
|
||||
|
||||
private final SimpleContainer simple = new SimpleContainer();
|
||||
|
||||
private final DirectContainer direct = new DirectContainer();
|
||||
|
||||
public ContainerType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(ContainerType containerType) {
|
||||
this.type = containerType;
|
||||
}
|
||||
|
||||
public SimpleContainer getSimple() {
|
||||
return this.simple;
|
||||
}
|
||||
|
||||
public DirectContainer getDirect() {
|
||||
return this.direct;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static abstract class AmqpContainer {
|
||||
|
||||
/**
|
||||
* Start the container automatically on startup.
|
||||
*/
|
||||
private boolean autoStartup = true;
|
||||
|
||||
/**
|
||||
* Acknowledge mode of container.
|
||||
*/
|
||||
private AcknowledgeMode acknowledgeMode;
|
||||
|
||||
/**
|
||||
* Number of messages to be handled in a single request. It should be greater than
|
||||
* or equal to the transaction size (if used).
|
||||
*/
|
||||
private Integer prefetch;
|
||||
|
||||
/**
|
||||
* Whether rejected deliveries are requeued by default; default true.
|
||||
*/
|
||||
private Boolean defaultRequeueRejected;
|
||||
|
||||
/**
|
||||
* How often idle container events should be published in milliseconds.
|
||||
*/
|
||||
private Long idleEventInterval;
|
||||
|
||||
/**
|
||||
* Optional properties for a retry interceptor.
|
||||
*/
|
||||
private final ListenerRetry retry = new ListenerRetry();
|
||||
|
||||
public boolean isAutoStartup() {
|
||||
return this.autoStartup;
|
||||
}
|
||||
|
||||
public void setAutoStartup(boolean autoStartup) {
|
||||
this.autoStartup = autoStartup;
|
||||
}
|
||||
|
||||
public AcknowledgeMode getAcknowledgeMode() {
|
||||
return this.acknowledgeMode;
|
||||
}
|
||||
|
||||
public void setAcknowledgeMode(AcknowledgeMode acknowledgeMode) {
|
||||
this.acknowledgeMode = acknowledgeMode;
|
||||
}
|
||||
|
||||
public Integer getPrefetch() {
|
||||
return this.prefetch;
|
||||
}
|
||||
|
||||
public void setPrefetch(Integer prefetch) {
|
||||
this.prefetch = prefetch;
|
||||
}
|
||||
|
||||
public Boolean getDefaultRequeueRejected() {
|
||||
return this.defaultRequeueRejected;
|
||||
}
|
||||
|
||||
public void setDefaultRequeueRejected(Boolean defaultRequeueRejected) {
|
||||
this.defaultRequeueRejected = defaultRequeueRejected;
|
||||
}
|
||||
|
||||
public Long getIdleEventInterval() {
|
||||
return this.idleEventInterval;
|
||||
}
|
||||
|
||||
public void setIdleEventInterval(Long idleEventInterval) {
|
||||
this.idleEventInterval = idleEventInterval;
|
||||
}
|
||||
|
||||
public ListenerRetry getRetry() {
|
||||
return this.retry;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration properties for {@code SimpleMessageListenerContainer}.
|
||||
*/
|
||||
public static class SimpleContainer extends AmqpContainer {
|
||||
|
||||
/**
|
||||
* Minimum number of listener invoker threads.
|
||||
*/
|
||||
private Integer concurrency;
|
||||
|
||||
/**
|
||||
* Maximum number of listener invoker threads.
|
||||
*/
|
||||
private Integer maxConcurrency;
|
||||
|
||||
/**
|
||||
* Number of messages to be processed in a transaction; number of messages between
|
||||
* acks. For best results it should be less than or equal to the prefetch count.
|
||||
*/
|
||||
private Integer transactionSize;
|
||||
|
||||
public Integer getConcurrency() {
|
||||
return this.concurrency;
|
||||
}
|
||||
|
||||
public void setConcurrency(Integer concurrency) {
|
||||
this.concurrency = concurrency;
|
||||
}
|
||||
|
||||
public Integer getMaxConcurrency() {
|
||||
return this.maxConcurrency;
|
||||
}
|
||||
|
||||
public void setMaxConcurrency(Integer maxConcurrency) {
|
||||
this.maxConcurrency = maxConcurrency;
|
||||
}
|
||||
|
||||
public Integer getTransactionSize() {
|
||||
return this.transactionSize;
|
||||
}
|
||||
|
||||
public void setTransactionSize(Integer transactionSize) {
|
||||
this.transactionSize = transactionSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration properties for {@code DirectMessageListenerContainer}.
|
||||
*/
|
||||
public static class DirectContainer extends AmqpContainer {
|
||||
|
||||
/**
|
||||
* Number of consumers per queue.
|
||||
*/
|
||||
private Integer consumersPerQueue;
|
||||
|
||||
public Integer getConsumersPerQueue() {
|
||||
return this.consumersPerQueue;
|
||||
}
|
||||
|
||||
public void setConsumersPerQueue(Integer consumersPerQueue) {
|
||||
this.consumersPerQueue = consumersPerQueue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Template {
|
||||
|
||||
private final Retry retry = new Retry();
|
||||
|
||||
/**
|
||||
* Enable mandatory messages. If a mandatory message cannot be routed to a queue
|
||||
* by the server, it will return an unroutable message with a Return method.
|
||||
*/
|
||||
private Boolean mandatory;
|
||||
|
||||
/**
|
||||
* Timeout for receive() operations.
|
||||
*/
|
||||
private Long receiveTimeout;
|
||||
|
||||
/**
|
||||
* Timeout for sendAndReceive() operations.
|
||||
*/
|
||||
private Long replyTimeout;
|
||||
|
||||
public Retry getRetry() {
|
||||
return this.retry;
|
||||
}
|
||||
|
||||
public Boolean getMandatory() {
|
||||
return this.mandatory;
|
||||
}
|
||||
|
||||
public void setMandatory(Boolean mandatory) {
|
||||
this.mandatory = mandatory;
|
||||
}
|
||||
|
||||
public Long getReceiveTimeout() {
|
||||
return this.receiveTimeout;
|
||||
}
|
||||
|
||||
public void setReceiveTimeout(Long receiveTimeout) {
|
||||
this.receiveTimeout = receiveTimeout;
|
||||
}
|
||||
|
||||
public Long getReplyTimeout() {
|
||||
return this.replyTimeout;
|
||||
}
|
||||
|
||||
public void setReplyTimeout(Long replyTimeout) {
|
||||
this.replyTimeout = replyTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Retry {
|
||||
|
||||
/**
|
||||
* Whether or not publishing retries are enabled.
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* Maximum number of attempts to publish or deliver a message.
|
||||
*/
|
||||
private int maxAttempts = 3;
|
||||
|
||||
/**
|
||||
* Interval between the first and second attempt to publish or deliver a message.
|
||||
*/
|
||||
private long initialInterval = 1000L;
|
||||
|
||||
/**
|
||||
* A multiplier to apply to the previous retry interval.
|
||||
*/
|
||||
private double multiplier = 1.0;
|
||||
|
||||
/**
|
||||
* Maximum interval between attempts.
|
||||
*/
|
||||
private long maxInterval = 10000L;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public int getMaxAttempts() {
|
||||
return this.maxAttempts;
|
||||
}
|
||||
|
||||
public void setMaxAttempts(int maxAttempts) {
|
||||
this.maxAttempts = maxAttempts;
|
||||
}
|
||||
|
||||
public long getInitialInterval() {
|
||||
return this.initialInterval;
|
||||
}
|
||||
|
||||
public void setInitialInterval(long initialInterval) {
|
||||
this.initialInterval = initialInterval;
|
||||
}
|
||||
|
||||
public double getMultiplier() {
|
||||
return this.multiplier;
|
||||
}
|
||||
|
||||
public void setMultiplier(double multiplier) {
|
||||
this.multiplier = multiplier;
|
||||
}
|
||||
|
||||
public long getMaxInterval() {
|
||||
return this.maxInterval;
|
||||
}
|
||||
|
||||
public void setMaxInterval(long maxInterval) {
|
||||
this.maxInterval = maxInterval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ListenerRetry extends Retry {
|
||||
|
||||
/**
|
||||
* Whether or not retries are stateless or stateful.
|
||||
*/
|
||||
private boolean stateless = true;
|
||||
|
||||
public boolean isStateless() {
|
||||
return this.stateless;
|
||||
}
|
||||
|
||||
public void setStateless(boolean stateless) {
|
||||
this.stateless = stateless;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class Address {
|
||||
|
||||
private static final String PREFIX_AMQP = "amqp://";
|
||||
|
||||
private static final int DEFAULT_PORT = 5672;
|
||||
|
||||
private String host;
|
||||
|
||||
private int port;
|
||||
|
||||
private String username;
|
||||
|
||||
private String password;
|
||||
|
||||
private String virtualHost;
|
||||
|
||||
private Address(String input) {
|
||||
input = input.trim();
|
||||
input = trimPrefix(input);
|
||||
input = parseUsernameAndPassword(input);
|
||||
input = parseVirtualHost(input);
|
||||
parseHostAndPort(input);
|
||||
}
|
||||
|
||||
private String trimPrefix(String input) {
|
||||
if (input.startsWith(PREFIX_AMQP)) {
|
||||
input = input.substring(PREFIX_AMQP.length());
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private String parseUsernameAndPassword(String input) {
|
||||
if (input.contains("@")) {
|
||||
String[] split = StringUtils.split(input, "@");
|
||||
String creds = split[0];
|
||||
input = split[1];
|
||||
split = StringUtils.split(creds, ":");
|
||||
this.username = split[0];
|
||||
if (split.length > 0) {
|
||||
this.password = split[1];
|
||||
}
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private String parseVirtualHost(String input) {
|
||||
int hostIndex = input.indexOf("/");
|
||||
if (hostIndex >= 0) {
|
||||
this.virtualHost = input.substring(hostIndex + 1);
|
||||
if (this.virtualHost.isEmpty()) {
|
||||
this.virtualHost = "/";
|
||||
}
|
||||
input = input.substring(0, hostIndex);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
private void parseHostAndPort(String input) {
|
||||
int portIndex = input.indexOf(':');
|
||||
if (portIndex == -1) {
|
||||
this.host = input;
|
||||
this.port = DEFAULT_PORT;
|
||||
}
|
||||
else {
|
||||
this.host = input.substring(0, portIndex);
|
||||
this.port = Integer.valueOf(input.substring(portIndex + 1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.amqp;
|
||||
|
||||
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
|
||||
/**
|
||||
* Configure {@link SimpleRabbitListenerContainerFactoryConfigurer} with sensible
|
||||
* defaults.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Gary Russell
|
||||
* @since 1.3.3
|
||||
*/
|
||||
public final class SimpleRabbitListenerContainerFactoryConfigurer extends
|
||||
AbstractRabbitListenerContainerFactoryConfigurer<SimpleRabbitListenerContainerFactory> {
|
||||
|
||||
@Override
|
||||
public void configure(SimpleRabbitListenerContainerFactory factory,
|
||||
ConnectionFactory connectionFactory) {
|
||||
RabbitProperties.SimpleContainer config = getRabbitProperties().getListener()
|
||||
.getSimple();
|
||||
configure(factory, connectionFactory, config);
|
||||
if (config.getConcurrency() != null) {
|
||||
factory.setConcurrentConsumers(config.getConcurrency());
|
||||
}
|
||||
if (config.getMaxConcurrency() != null) {
|
||||
factory.setMaxConcurrentConsumers(config.getMaxConcurrency());
|
||||
}
|
||||
if (config.getTransactionSize() != null) {
|
||||
factory.setTxSize(config.getTransactionSize());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for RabbitMQ.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.amqp;
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.aop;
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.Advice;
|
||||
import org.aspectj.weaver.AnnotatedElement;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
* Auto-configuration} for Spring's AOP support. Equivalent to enabling
|
||||
* {@link org.springframework.context.annotation.EnableAspectJAutoProxy} in your
|
||||
* configuration.
|
||||
* <p>
|
||||
* The configuration will not be activated if {@literal spring.aop.auto=false}. The
|
||||
* {@literal proxyTargetClass} attribute will be {@literal false}, by default, but can be
|
||||
* overridden by specifying {@literal spring.aop.proxyTargetClass=true}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Josh Long
|
||||
* @see EnableAspectJAutoProxy
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class,
|
||||
AnnotatedElement.class })
|
||||
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
|
||||
public class AopAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = false)
|
||||
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
|
||||
public static class JdkDynamicAutoProxyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true)
|
||||
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
|
||||
public static class CglibAutoProxyConfiguration {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Spring AOP.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.aop;
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.explore.support.JobExplorerFactoryBean;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.launch.support.SimpleJobLauncher;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.batch.core.repository.support.JobRepositoryFactoryBean;
|
||||
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Basic {@link BatchConfigurer} implementation.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Andy Wilkinson
|
||||
* @author Kazuki Shimizu
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class BasicBatchConfigurer implements BatchConfigurer {
|
||||
|
||||
private final BatchProperties properties;
|
||||
|
||||
private final DataSource dataSource;
|
||||
|
||||
private PlatformTransactionManager transactionManager;
|
||||
|
||||
private final TransactionManagerCustomizers transactionManagerCustomizers;
|
||||
|
||||
private JobRepository jobRepository;
|
||||
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
private JobExplorer jobExplorer;
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicBatchConfigurer} instance.
|
||||
* @param properties the batch properties
|
||||
* @param dataSource the underlying data source
|
||||
* @param transactionManagerCustomizers transaction manager customizers (or
|
||||
* {@code null})
|
||||
*/
|
||||
protected BasicBatchConfigurer(BatchProperties properties, DataSource dataSource,
|
||||
TransactionManagerCustomizers transactionManagerCustomizers) {
|
||||
this.properties = properties;
|
||||
this.dataSource = dataSource;
|
||||
this.transactionManagerCustomizers = transactionManagerCustomizers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobRepository getJobRepository() {
|
||||
return this.jobRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformTransactionManager getTransactionManager() {
|
||||
return this.transactionManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobLauncher getJobLauncher() {
|
||||
return this.jobLauncher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JobExplorer getJobExplorer() throws Exception {
|
||||
return this.jobExplorer;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() {
|
||||
try {
|
||||
this.transactionManager = buildTransactionManager();
|
||||
this.jobRepository = createJobRepository();
|
||||
this.jobLauncher = createJobLauncher();
|
||||
this.jobExplorer = createJobExplorer();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to initialize Spring Batch", ex);
|
||||
}
|
||||
}
|
||||
|
||||
protected JobExplorer createJobExplorer() throws Exception {
|
||||
JobExplorerFactoryBean jobExplorerFactoryBean = new JobExplorerFactoryBean();
|
||||
jobExplorerFactoryBean.setDataSource(this.dataSource);
|
||||
String tablePrefix = this.properties.getTablePrefix();
|
||||
if (StringUtils.hasText(tablePrefix)) {
|
||||
jobExplorerFactoryBean.setTablePrefix(tablePrefix);
|
||||
}
|
||||
jobExplorerFactoryBean.afterPropertiesSet();
|
||||
return jobExplorerFactoryBean.getObject();
|
||||
}
|
||||
|
||||
protected JobLauncher createJobLauncher() throws Exception {
|
||||
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
|
||||
jobLauncher.setJobRepository(getJobRepository());
|
||||
jobLauncher.afterPropertiesSet();
|
||||
return jobLauncher;
|
||||
}
|
||||
|
||||
protected JobRepository createJobRepository() throws Exception {
|
||||
JobRepositoryFactoryBean factory = new JobRepositoryFactoryBean();
|
||||
factory.setDataSource(this.dataSource);
|
||||
String isolationLevel = determineIsolationLevel();
|
||||
if (isolationLevel != null) {
|
||||
factory.setIsolationLevelForCreate(isolationLevel);
|
||||
}
|
||||
String tablePrefix = this.properties.getTablePrefix();
|
||||
if (StringUtils.hasText(tablePrefix)) {
|
||||
factory.setTablePrefix(tablePrefix);
|
||||
}
|
||||
factory.setTransactionManager(getTransactionManager());
|
||||
factory.afterPropertiesSet();
|
||||
return factory.getObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the isolation level for create* operation of the {@link JobRepository}.
|
||||
* @return the isolation level or {@code null} to use the default
|
||||
*/
|
||||
protected String determineIsolationLevel() {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected PlatformTransactionManager createTransactionManager() {
|
||||
return new DataSourceTransactionManager(this.dataSource);
|
||||
}
|
||||
|
||||
private PlatformTransactionManager buildTransactionManager() {
|
||||
PlatformTransactionManager transactionManager = createTransactionManager();
|
||||
if (this.transactionManagerCustomizers != null) {
|
||||
this.transactionManagerCustomizers.customize(transactionManager);
|
||||
}
|
||||
return transactionManager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.core.configuration.ListableJobLocator;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.launch.JobOperator;
|
||||
import org.springframework.batch.core.launch.support.SimpleJobOperator;
|
||||
import org.springframework.batch.core.repository.JobRepository;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.ExitCodeGenerator;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Batch. By default a
|
||||
* Runner will be created and all jobs in the context will be executed on startup.
|
||||
* <p>
|
||||
* Disable this behavior with {@literal spring.batch.job.enabled=false}).
|
||||
* <p>
|
||||
* Alternatively, discrete Job names to execute on startup can be supplied by the User
|
||||
* with a comma-delimited list: {@literal spring.batch.job.names=job1,job2}. In this case
|
||||
* the Runner will first find jobs registered as Beans, then those in the existing
|
||||
* JobRegistry.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Eddú Meléndez
|
||||
* @author Kazuki Shimizu
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ JobLauncher.class, DataSource.class, JdbcOperations.class })
|
||||
@AutoConfigureAfter(HibernateJpaAutoConfiguration.class)
|
||||
@ConditionalOnBean(JobLauncher.class)
|
||||
@EnableConfigurationProperties(BatchProperties.class)
|
||||
@Import(BatchConfigurerConfiguration.class)
|
||||
public class BatchAutoConfiguration {
|
||||
|
||||
private final BatchProperties properties;
|
||||
|
||||
private final JobParametersConverter jobParametersConverter;
|
||||
|
||||
public BatchAutoConfiguration(BatchProperties properties,
|
||||
ObjectProvider<JobParametersConverter> jobParametersConverter) {
|
||||
this.properties = properties;
|
||||
this.jobParametersConverter = jobParametersConverter.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnBean(DataSource.class)
|
||||
public BatchDatabaseInitializer batchDatabaseInitializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
return new BatchDatabaseInitializer(dataSource, resourceLoader, this.properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@ConditionalOnProperty(prefix = "spring.batch.job", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
public JobLauncherCommandLineRunner jobLauncherCommandLineRunner(
|
||||
JobLauncher jobLauncher, JobExplorer jobExplorer) {
|
||||
JobLauncherCommandLineRunner runner = new JobLauncherCommandLineRunner(
|
||||
jobLauncher, jobExplorer);
|
||||
String jobNames = this.properties.getJob().getNames();
|
||||
if (StringUtils.hasText(jobNames)) {
|
||||
runner.setJobNames(jobNames);
|
||||
}
|
||||
return runner;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(ExitCodeGenerator.class)
|
||||
public JobExecutionExitCodeGenerator jobExecutionExitCodeGenerator() {
|
||||
return new JobExecutionExitCodeGenerator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(JobOperator.class)
|
||||
public SimpleJobOperator jobOperator(JobExplorer jobExplorer, JobLauncher jobLauncher,
|
||||
ListableJobLocator jobRegistry, JobRepository jobRepository)
|
||||
throws Exception {
|
||||
SimpleJobOperator factory = new SimpleJobOperator();
|
||||
factory.setJobExplorer(jobExplorer);
|
||||
factory.setJobLauncher(jobLauncher);
|
||||
factory.setJobRegistry(jobRegistry);
|
||||
factory.setJobRepository(jobRepository);
|
||||
if (this.jobParametersConverter != null) {
|
||||
factory.setJobParametersConverter(this.jobParametersConverter);
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Provide a {@link BatchConfigurer} according to the current environment.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConditionalOnClass(PlatformTransactionManager.class)
|
||||
@ConditionalOnMissingBean(BatchConfigurer.class)
|
||||
@Configuration
|
||||
class BatchConfigurerConfiguration {
|
||||
|
||||
@ConditionalOnMissingBean(name = "entityManagerFactory")
|
||||
static class JdbcBatchConfiguration {
|
||||
|
||||
@Bean
|
||||
public BasicBatchConfigurer batchConfigurer(BatchProperties properties,
|
||||
DataSource dataSource,
|
||||
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
return new BasicBatchConfigurer(properties, dataSource,
|
||||
transactionManagerCustomizers.getIfAvailable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnClass(name = "javax.persistence.EntityManagerFactory")
|
||||
@ConditionalOnBean(name = "entityManagerFactory")
|
||||
static class JpaBatchConfiguration {
|
||||
|
||||
@Bean
|
||||
public JpaBatchConfigurer batchConfigurer(BatchProperties properties,
|
||||
DataSource dataSource,
|
||||
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers,
|
||||
EntityManagerFactory entityManagerFactory) {
|
||||
return new JpaBatchConfigurer(properties, dataSource,
|
||||
transactionManagerCustomizers.getIfAvailable(), entityManagerFactory);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AbstractDatabaseInitializer;
|
||||
import org.springframework.boot.autoconfigure.DatabaseInitializationMode;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Initialize the Spring Batch schema (ignoring errors, so should be idempotent).
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Vedran Pavic
|
||||
*/
|
||||
public class BatchDatabaseInitializer extends AbstractDatabaseInitializer {
|
||||
|
||||
private final BatchProperties properties;
|
||||
|
||||
public BatchDatabaseInitializer(DataSource dataSource, ResourceLoader resourceLoader,
|
||||
BatchProperties properties) {
|
||||
super(dataSource, resourceLoader);
|
||||
Assert.notNull(properties, "BatchProperties must not be null");
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DatabaseInitializationMode getMode() {
|
||||
return this.properties.getInitializeSchema();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSchemaLocation() {
|
||||
return this.properties.getSchema();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDatabaseName() {
|
||||
String databaseName = super.getDatabaseName();
|
||||
if ("oracle".equals(databaseName)) {
|
||||
return "oracle10g";
|
||||
}
|
||||
return databaseName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import org.springframework.boot.autoconfigure.DatabaseInitializationMode;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for Spring Batch.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Eddú Meléndez
|
||||
* @author Vedran Pavic
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.batch")
|
||||
public class BatchProperties {
|
||||
|
||||
private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/springframework/"
|
||||
+ "batch/core/schema-@@platform@@.sql";
|
||||
|
||||
/**
|
||||
* Path to the SQL file to use to initialize the database schema.
|
||||
*/
|
||||
private String schema = DEFAULT_SCHEMA_LOCATION;
|
||||
|
||||
/**
|
||||
* Table prefix for all the batch meta-data tables.
|
||||
*/
|
||||
private String tablePrefix;
|
||||
|
||||
/**
|
||||
* Database schema initialization mode.
|
||||
*/
|
||||
private DatabaseInitializationMode initializeSchema = DatabaseInitializationMode.EMBEDDED;
|
||||
|
||||
private final Job job = new Job();
|
||||
|
||||
public String getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public void setSchema(String schema) {
|
||||
this.schema = schema;
|
||||
}
|
||||
|
||||
public String getTablePrefix() {
|
||||
return this.tablePrefix;
|
||||
}
|
||||
|
||||
public void setTablePrefix(String tablePrefix) {
|
||||
this.tablePrefix = tablePrefix;
|
||||
}
|
||||
|
||||
public DatabaseInitializationMode getInitializeSchema() {
|
||||
return this.initializeSchema;
|
||||
}
|
||||
|
||||
public void setInitializeSchema(DatabaseInitializationMode initializeSchema) {
|
||||
this.initializeSchema = initializeSchema;
|
||||
}
|
||||
|
||||
public Job getJob() {
|
||||
return this.job;
|
||||
}
|
||||
|
||||
public static class Job {
|
||||
|
||||
/**
|
||||
* Comma-separated list of job names to execute on startup. By default, all Jobs
|
||||
* found in the context are executed.
|
||||
*/
|
||||
private String names = "";
|
||||
|
||||
public String getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
public void setNames(String names) {
|
||||
this.names = names;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* Spring {@link ApplicationEvent} encapsulating a {@link JobExecution}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class JobExecutionEvent extends ApplicationEvent {
|
||||
|
||||
private final JobExecution execution;
|
||||
|
||||
/**
|
||||
* Create a new {@link JobExecutionEvent} instance.
|
||||
* @param execution the job execution
|
||||
*/
|
||||
public JobExecutionEvent(JobExecution execution) {
|
||||
super(execution);
|
||||
this.execution = execution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the job execution.
|
||||
* @return the job execution
|
||||
*/
|
||||
public JobExecution getJobExecution() {
|
||||
return this.execution;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.boot.ExitCodeGenerator;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
|
||||
/**
|
||||
* {@link ExitCodeGenerator} for {@link JobExecutionEvent}s.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class JobExecutionExitCodeGenerator
|
||||
implements ApplicationListener<JobExecutionEvent>, ExitCodeGenerator {
|
||||
|
||||
private final List<JobExecution> executions = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(JobExecutionEvent event) {
|
||||
this.executions.add(event.getJobExecution());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExitCode() {
|
||||
for (JobExecution execution : this.executions) {
|
||||
if (execution.getStatus().ordinal() > 0) {
|
||||
return execution.getStatus().ordinal();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.batch.core.BatchStatus;
|
||||
import org.springframework.batch.core.Job;
|
||||
import org.springframework.batch.core.JobExecution;
|
||||
import org.springframework.batch.core.JobExecutionException;
|
||||
import org.springframework.batch.core.JobInstance;
|
||||
import org.springframework.batch.core.JobParameter;
|
||||
import org.springframework.batch.core.JobParameters;
|
||||
import org.springframework.batch.core.JobParametersIncrementer;
|
||||
import org.springframework.batch.core.JobParametersInvalidException;
|
||||
import org.springframework.batch.core.configuration.JobRegistry;
|
||||
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
|
||||
import org.springframework.batch.core.converter.JobParametersConverter;
|
||||
import org.springframework.batch.core.explore.JobExplorer;
|
||||
import org.springframework.batch.core.launch.JobLauncher;
|
||||
import org.springframework.batch.core.launch.JobParametersNotFoundException;
|
||||
import org.springframework.batch.core.launch.NoSuchJobException;
|
||||
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
|
||||
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
|
||||
import org.springframework.batch.core.repository.JobRestartException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link CommandLineRunner} to {@link JobLauncher launch} Spring Batch jobs. Runs all
|
||||
* jobs in the surrounding context by default. Can also be used to launch a specific job
|
||||
* by providing a jobName
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Jean-Pierre Bergamin
|
||||
*/
|
||||
public class JobLauncherCommandLineRunner
|
||||
implements CommandLineRunner, ApplicationEventPublisherAware {
|
||||
|
||||
private static final Log logger = LogFactory
|
||||
.getLog(JobLauncherCommandLineRunner.class);
|
||||
|
||||
private JobParametersConverter converter = new DefaultJobParametersConverter();
|
||||
|
||||
private JobLauncher jobLauncher;
|
||||
|
||||
private JobRegistry jobRegistry;
|
||||
|
||||
private JobExplorer jobExplorer;
|
||||
|
||||
private String jobNames;
|
||||
|
||||
private Collection<Job> jobs = Collections.emptySet();
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
public JobLauncherCommandLineRunner(JobLauncher jobLauncher,
|
||||
JobExplorer jobExplorer) {
|
||||
this.jobLauncher = jobLauncher;
|
||||
this.jobExplorer = jobExplorer;
|
||||
}
|
||||
|
||||
public void setJobNames(String jobNames) {
|
||||
this.jobNames = jobNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setJobRegistry(JobRegistry jobRegistry) {
|
||||
this.jobRegistry = jobRegistry;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setJobParametersConverter(JobParametersConverter converter) {
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Autowired(required = false)
|
||||
public void setJobs(Collection<Job> jobs) {
|
||||
this.jobs = jobs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws JobExecutionException {
|
||||
logger.info("Running default command line with: " + Arrays.asList(args));
|
||||
launchJobFromProperties(StringUtils.splitArrayElementsIntoProperties(args, "="));
|
||||
}
|
||||
|
||||
protected void launchJobFromProperties(Properties properties)
|
||||
throws JobExecutionException {
|
||||
JobParameters jobParameters = this.converter.getJobParameters(properties);
|
||||
executeLocalJobs(jobParameters);
|
||||
executeRegisteredJobs(jobParameters);
|
||||
}
|
||||
|
||||
private JobParameters getNextJobParameters(Job job,
|
||||
JobParameters additionalParameters) {
|
||||
String name = job.getName();
|
||||
JobParameters parameters = new JobParameters();
|
||||
List<JobInstance> lastInstances = this.jobExplorer.getJobInstances(name, 0, 1);
|
||||
JobParametersIncrementer incrementer = job.getJobParametersIncrementer();
|
||||
Map<String, JobParameter> additionals = additionalParameters.getParameters();
|
||||
if (lastInstances.isEmpty()) {
|
||||
// Start from a completely clean sheet
|
||||
if (incrementer != null) {
|
||||
parameters = incrementer.getNext(new JobParameters());
|
||||
}
|
||||
}
|
||||
else {
|
||||
List<JobExecution> previousExecutions = this.jobExplorer
|
||||
.getJobExecutions(lastInstances.get(0));
|
||||
JobExecution previousExecution = previousExecutions.get(0);
|
||||
if (previousExecution == null) {
|
||||
// Normally this will not happen - an instance exists with no executions
|
||||
if (incrementer != null) {
|
||||
parameters = incrementer.getNext(new JobParameters());
|
||||
}
|
||||
}
|
||||
else if (isStoppedOrFailed(previousExecution) && job.isRestartable()) {
|
||||
// Retry a failed or stopped execution
|
||||
parameters = previousExecution.getJobParameters();
|
||||
// Non-identifying additional parameters can be removed to a retry
|
||||
removeNonIdentifying(additionals);
|
||||
}
|
||||
else if (incrementer != null) {
|
||||
// New instance so increment the parameters if we can
|
||||
parameters = incrementer.getNext(previousExecution.getJobParameters());
|
||||
}
|
||||
}
|
||||
return merge(parameters, additionals);
|
||||
}
|
||||
|
||||
private boolean isStoppedOrFailed(JobExecution execution) {
|
||||
BatchStatus status = execution.getStatus();
|
||||
return (status == BatchStatus.STOPPED || status == BatchStatus.FAILED);
|
||||
}
|
||||
|
||||
private void removeNonIdentifying(Map<String, JobParameter> parameters) {
|
||||
HashMap<String, JobParameter> copy = new HashMap<>(parameters);
|
||||
for (Map.Entry<String, JobParameter> parameter : copy.entrySet()) {
|
||||
if (!parameter.getValue().isIdentifying()) {
|
||||
parameters.remove(parameter.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private JobParameters merge(JobParameters parameters,
|
||||
Map<String, JobParameter> additionals) {
|
||||
Map<String, JobParameter> merged = new HashMap<>();
|
||||
merged.putAll(parameters.getParameters());
|
||||
merged.putAll(additionals);
|
||||
parameters = new JobParameters(merged);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private void executeRegisteredJobs(JobParameters jobParameters)
|
||||
throws JobExecutionException {
|
||||
if (this.jobRegistry != null && StringUtils.hasText(this.jobNames)) {
|
||||
String[] jobsToRun = this.jobNames.split(",");
|
||||
for (String jobName : jobsToRun) {
|
||||
try {
|
||||
Job job = this.jobRegistry.getJob(jobName);
|
||||
if (this.jobs.contains(job)) {
|
||||
continue;
|
||||
}
|
||||
execute(job, jobParameters);
|
||||
}
|
||||
catch (NoSuchJobException ex) {
|
||||
logger.debug("No job found in registry for job name: " + jobName);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void execute(Job job, JobParameters jobParameters)
|
||||
throws JobExecutionAlreadyRunningException, JobRestartException,
|
||||
JobInstanceAlreadyCompleteException, JobParametersInvalidException,
|
||||
JobParametersNotFoundException {
|
||||
JobParameters nextParameters = getNextJobParameters(job, jobParameters);
|
||||
if (nextParameters != null) {
|
||||
JobExecution execution = this.jobLauncher.run(job, nextParameters);
|
||||
if (this.publisher != null) {
|
||||
this.publisher.publishEvent(new JobExecutionEvent(execution));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeLocalJobs(JobParameters jobParameters)
|
||||
throws JobExecutionException {
|
||||
for (Job job : this.jobs) {
|
||||
if (StringUtils.hasText(this.jobNames)) {
|
||||
String[] jobsToRun = this.jobNames.split(",");
|
||||
if (!PatternMatchUtils.simpleMatch(jobsToRun, job.getName())) {
|
||||
logger.debug("Skipped job: " + job.getName());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
execute(job, jobParameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.batch;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* A {@link BasicBatchConfigurer} tailored for JPA.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class JpaBatchConfigurer extends BasicBatchConfigurer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(JpaBatchConfigurer.class);
|
||||
|
||||
private final EntityManagerFactory entityManagerFactory;
|
||||
|
||||
/**
|
||||
* Create a new {@link BasicBatchConfigurer} instance.
|
||||
* @param properties the batch properties
|
||||
* @param dataSource the underlying data source
|
||||
* @param transactionManagerCustomizers transaction manager customizers (or
|
||||
* {@code null})
|
||||
* @param entityManagerFactory the entity manager factory (or {@code null})
|
||||
*/
|
||||
protected JpaBatchConfigurer(BatchProperties properties, DataSource dataSource,
|
||||
TransactionManagerCustomizers transactionManagerCustomizers,
|
||||
EntityManagerFactory entityManagerFactory) {
|
||||
super(properties, dataSource, transactionManagerCustomizers);
|
||||
this.entityManagerFactory = entityManagerFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String determineIsolationLevel() {
|
||||
logger.warn(
|
||||
"JPA does not support custom isolation levels, so locks may not be taken when launching Jobs");
|
||||
return "ISOLATION_DEFAULT";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PlatformTransactionManager createTransactionManager() {
|
||||
return new JpaTransactionManager(this.entityManagerFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Spring Batch.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.batch;
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.CacheConfigurationImportSelector;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.jpa.EntityManagerFactoryDependsOnPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.annotation.EnableCaching;
|
||||
import org.springframework.cache.interceptor.CacheAspectSupport;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.ImportSelector;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.orm.jpa.AbstractEntityManagerFactoryBean;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for the cache abstraction. Creates a
|
||||
* {@link CacheManager} if necessary when caching is enabled via {@link EnableCaching}.
|
||||
* <p>
|
||||
* Cache store can be auto-detected or specified explicitly via configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
* @see EnableCaching
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(CacheManager.class)
|
||||
@ConditionalOnBean(CacheAspectSupport.class)
|
||||
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
|
||||
@EnableConfigurationProperties(CacheProperties.class)
|
||||
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
|
||||
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
|
||||
RedisAutoConfiguration.class })
|
||||
@Import(CacheConfigurationImportSelector.class)
|
||||
public class CacheAutoConfiguration {
|
||||
|
||||
static final String VALIDATOR_BEAN_NAME = "cacheAutoConfigurationValidator";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CacheManagerCustomizers cacheManagerCustomizers(
|
||||
ObjectProvider<List<CacheManagerCustomizer<?>>> customizers) {
|
||||
return new CacheManagerCustomizers(customizers.getIfAvailable());
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
public static CacheManagerValidatorPostProcessor cacheAutoConfigurationValidatorPostProcessor() {
|
||||
return new CacheManagerValidatorPostProcessor();
|
||||
}
|
||||
|
||||
@Bean(name = VALIDATOR_BEAN_NAME)
|
||||
public CacheManagerValidator cacheAutoConfigurationValidator() {
|
||||
return new CacheManagerValidator();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnClass(LocalContainerEntityManagerFactoryBean.class)
|
||||
@ConditionalOnBean(AbstractEntityManagerFactoryBean.class)
|
||||
protected static class CacheManagerJpaDependencyConfiguration
|
||||
extends EntityManagerFactoryDependsOnPostProcessor {
|
||||
|
||||
public CacheManagerJpaDependencyConfiguration() {
|
||||
super("cacheManager");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryPostProcessor} to ensure that the {@link CacheManagerValidator}
|
||||
* is triggered before {@link CacheAspectSupport} but without causing early
|
||||
* instantiation.
|
||||
*/
|
||||
static class CacheManagerValidatorPostProcessor implements BeanFactoryPostProcessor {
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
|
||||
throws BeansException {
|
||||
for (String name : beanFactory.getBeanNamesForType(CacheAspectSupport.class,
|
||||
false, false)) {
|
||||
BeanDefinition definition = beanFactory.getBeanDefinition(name);
|
||||
definition.setDependsOn(
|
||||
append(definition.getDependsOn(), VALIDATOR_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private String[] append(String[] array, String value) {
|
||||
String[] result = new String[array == null ? 1 : array.length + 1];
|
||||
if (array != null) {
|
||||
System.arraycopy(array, 0, result, 0, array.length);
|
||||
}
|
||||
result[result.length - 1] = value;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean used to validate that a CacheManager exists and provide a more meaningful
|
||||
* exception.
|
||||
*/
|
||||
static class CacheManagerValidator {
|
||||
|
||||
@Autowired
|
||||
private CacheProperties cacheProperties;
|
||||
|
||||
@Autowired(required = false)
|
||||
private CacheManager cacheManager;
|
||||
|
||||
@PostConstruct
|
||||
public void checkHasCacheManager() {
|
||||
Assert.notNull(this.cacheManager,
|
||||
"No cache manager could "
|
||||
+ "be auto-configured, check your configuration (caching "
|
||||
+ "type is '" + this.cacheProperties.getType() + "')");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ImportSelector} to add {@link CacheType} configuration classes.
|
||||
*/
|
||||
static class CacheConfigurationImportSelector implements ImportSelector {
|
||||
|
||||
@Override
|
||||
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
|
||||
CacheType[] types = CacheType.values();
|
||||
String[] imports = new String[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
|
||||
}
|
||||
return imports;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 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.SpringBootCondition;
|
||||
import org.springframework.boot.context.properties.bind.BindException;
|
||||
import org.springframework.boot.context.properties.bind.BindResult;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.ClassMetadata;
|
||||
|
||||
/**
|
||||
* General cache condition used with all cache configuration classes.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
* @since 1.3.0
|
||||
*/
|
||||
class CacheCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
String sourceClass = "";
|
||||
if (metadata instanceof ClassMetadata) {
|
||||
sourceClass = ((ClassMetadata) metadata).getClassName();
|
||||
}
|
||||
ConditionMessage.Builder message = ConditionMessage.forCondition("Cache",
|
||||
sourceClass);
|
||||
Environment environment = context.getEnvironment();
|
||||
try {
|
||||
BindResult<CacheType> specified = Binder.get(environment)
|
||||
.bind("spring.cache.type", CacheType.class);
|
||||
if (!specified.isBound()) {
|
||||
return ConditionOutcome.match(message.because("automatic cache type"));
|
||||
}
|
||||
CacheType required = CacheConfigurations
|
||||
.getType(((AnnotationMetadata) metadata).getClassName());
|
||||
if (specified.get() == required) {
|
||||
return ConditionOutcome
|
||||
.match(message.because(specified.get() + " cache type"));
|
||||
}
|
||||
}
|
||||
catch (BindException ex) {
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.because("unknown cache type"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Mappings between {@link CacheType} and {@code @Configuration}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Eddú Meléndez
|
||||
*/
|
||||
final class CacheConfigurations {
|
||||
|
||||
private static final Map<CacheType, Class<?>> MAPPINGS;
|
||||
|
||||
static {
|
||||
Map<CacheType, Class<?>> mappings = new HashMap<>();
|
||||
mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
|
||||
mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
|
||||
mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
|
||||
mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
|
||||
mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
|
||||
mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
|
||||
mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
|
||||
mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
|
||||
mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
|
||||
mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
|
||||
MAPPINGS = Collections.unmodifiableMap(mappings);
|
||||
}
|
||||
|
||||
private CacheConfigurations() {
|
||||
}
|
||||
|
||||
public static String getConfigurationClass(CacheType cacheType) {
|
||||
Class<?> configurationClass = MAPPINGS.get(cacheType);
|
||||
Assert.state(configurationClass != null, "Unknown cache type " + cacheType);
|
||||
return configurationClass.getName();
|
||||
}
|
||||
|
||||
public static CacheType getType(String configurationClassName) {
|
||||
for (Map.Entry<CacheType, Class<?>> entry : MAPPINGS.entrySet()) {
|
||||
if (entry.getValue().getName().equals(configurationClassName)) {
|
||||
return entry.getKey();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Unknown configuration class " + configurationClassName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize the cache
|
||||
* manager before it is fully initialized, in particular to tune its configuration.
|
||||
*
|
||||
* @param <T> the type of the {@link CacheManager}
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.3
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CacheManagerCustomizer<T extends CacheManager> {
|
||||
|
||||
/**
|
||||
* Customize the cache manager.
|
||||
* @param cacheManager the {@code CacheManager} to customize
|
||||
*/
|
||||
void customize(T cacheManager);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.core.ResolvableType;
|
||||
|
||||
/**
|
||||
* Invokes the available {@link CacheManagerCustomizer} instances in the context for a
|
||||
* given {@link CacheManager}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.5.0
|
||||
*/
|
||||
public class CacheManagerCustomizers {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(CacheManagerCustomizers.class);
|
||||
|
||||
private final List<CacheManagerCustomizer<?>> customizers;
|
||||
|
||||
public CacheManagerCustomizers(
|
||||
List<? extends CacheManagerCustomizer<?>> customizers) {
|
||||
this.customizers = (customizers != null ? new ArrayList<>(customizers)
|
||||
: Collections.<CacheManagerCustomizer<?>>emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Customize the specified {@link CacheManager}. Locates all
|
||||
* {@link CacheManagerCustomizer} beans able to handle the specified instance and
|
||||
* invoke {@link CacheManagerCustomizer#customize(CacheManager)} on them.
|
||||
* @param <T> the type of cache manager
|
||||
* @param cacheManager the cache manager to customize
|
||||
* @return the cache manager
|
||||
*/
|
||||
public <T extends CacheManager> T customize(T cacheManager) {
|
||||
for (CacheManagerCustomizer<?> customizer : this.customizers) {
|
||||
Class<?> generic = ResolvableType
|
||||
.forClass(CacheManagerCustomizer.class, customizer.getClass())
|
||||
.resolveGeneric();
|
||||
if (generic.isInstance(cacheManager)) {
|
||||
customize(cacheManager, customizer);
|
||||
}
|
||||
}
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private void customize(CacheManager cacheManager, CacheManagerCustomizer customizer) {
|
||||
try {
|
||||
customizer.customize(cacheManager);
|
||||
}
|
||||
catch (ClassCastException ex) {
|
||||
// Possibly a lambda-defined customizer which we could not resolve the generic
|
||||
// cache manager type for
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(
|
||||
"Non-matching cache manager type for customizer: " + customizer,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Configuration properties for the cache abstraction.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.cache")
|
||||
public class CacheProperties {
|
||||
|
||||
/**
|
||||
* Cache type, auto-detected according to the environment by default.
|
||||
*/
|
||||
private CacheType type;
|
||||
|
||||
/**
|
||||
* Comma-separated list of cache names to create if supported by the underlying cache
|
||||
* manager. Usually, this disables the ability to create additional caches on-the-fly.
|
||||
*/
|
||||
private List<String> cacheNames = new ArrayList<>();
|
||||
|
||||
private final Caffeine caffeine = new Caffeine();
|
||||
|
||||
private final Couchbase couchbase = new Couchbase();
|
||||
|
||||
private final EhCache ehcache = new EhCache();
|
||||
|
||||
private final Infinispan infinispan = new Infinispan();
|
||||
|
||||
private final JCache jcache = new JCache();
|
||||
|
||||
public CacheType getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public void setType(CacheType mode) {
|
||||
this.type = mode;
|
||||
}
|
||||
|
||||
public List<String> getCacheNames() {
|
||||
return this.cacheNames;
|
||||
}
|
||||
|
||||
public void setCacheNames(List<String> cacheNames) {
|
||||
this.cacheNames = cacheNames;
|
||||
}
|
||||
|
||||
public Caffeine getCaffeine() {
|
||||
return this.caffeine;
|
||||
}
|
||||
|
||||
public Couchbase getCouchbase() {
|
||||
return this.couchbase;
|
||||
}
|
||||
|
||||
public EhCache getEhcache() {
|
||||
return this.ehcache;
|
||||
}
|
||||
|
||||
public Infinispan getInfinispan() {
|
||||
return this.infinispan;
|
||||
}
|
||||
|
||||
public JCache getJcache() {
|
||||
return this.jcache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the config location if set.
|
||||
* @param config the config resource
|
||||
* @return the location or {@code null} if it is not set
|
||||
* @throws IllegalArgumentException if the config attribute is set to an unknown
|
||||
* location
|
||||
*/
|
||||
public Resource resolveConfigLocation(Resource config) {
|
||||
if (config != null) {
|
||||
Assert.isTrue(config.exists(), "Cache configuration does not exist '"
|
||||
+ config.getDescription() + "'");
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Caffeine specific cache properties.
|
||||
*/
|
||||
public static class Caffeine {
|
||||
|
||||
/**
|
||||
* The spec to use to create caches. Check CaffeineSpec for more details on the
|
||||
* spec format.
|
||||
*/
|
||||
private String spec;
|
||||
|
||||
public String getSpec() {
|
||||
return this.spec;
|
||||
}
|
||||
|
||||
public void setSpec(String spec) {
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Couchbase specific cache properties.
|
||||
*/
|
||||
public static class Couchbase {
|
||||
|
||||
/**
|
||||
* Entry expiration in milliseconds. By default the entries never expire. Note
|
||||
* that this value is ultimately converted to seconds.
|
||||
*/
|
||||
private int expiration;
|
||||
|
||||
public int getExpiration() {
|
||||
return this.expiration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the expiration in seconds.
|
||||
* @return the expiration in seconds
|
||||
*/
|
||||
public int getExpirationSeconds() {
|
||||
return (int) TimeUnit.MILLISECONDS.toSeconds(this.expiration);
|
||||
}
|
||||
|
||||
public void setExpiration(int expiration) {
|
||||
this.expiration = expiration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* EhCache specific cache properties.
|
||||
*/
|
||||
public static class EhCache {
|
||||
|
||||
/**
|
||||
* The location of the configuration file to use to initialize EhCache.
|
||||
*/
|
||||
private Resource config;
|
||||
|
||||
public Resource getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public void setConfig(Resource config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Infinispan specific cache properties.
|
||||
*/
|
||||
public static class Infinispan {
|
||||
|
||||
/**
|
||||
* The location of the configuration file to use to initialize Infinispan.
|
||||
*/
|
||||
private Resource config;
|
||||
|
||||
public Resource getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public void setConfig(Resource config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* JCache (JSR-107) specific cache properties.
|
||||
*/
|
||||
public static class JCache {
|
||||
|
||||
/**
|
||||
* The location of the configuration file to use to initialize the cache manager.
|
||||
* The configuration file is dependent of the underlying cache implementation.
|
||||
*/
|
||||
private Resource config;
|
||||
|
||||
/**
|
||||
* Fully qualified name of the CachingProvider implementation to use to retrieve
|
||||
* the JSR-107 compliant cache manager. Only needed if more than one JSR-107
|
||||
* implementation is available on the classpath.
|
||||
*/
|
||||
private String provider;
|
||||
|
||||
public String getProvider() {
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
public void setProvider(String provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public Resource getConfig() {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public void setConfig(Resource config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
/**
|
||||
* Supported cache types (defined in order of precedence).
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public enum CacheType {
|
||||
|
||||
/**
|
||||
* Generic caching using 'Cache' beans from the context.
|
||||
*/
|
||||
GENERIC,
|
||||
|
||||
/**
|
||||
* JCache (JSR-107) backed caching.
|
||||
*/
|
||||
JCACHE,
|
||||
|
||||
/**
|
||||
* EhCache backed caching.
|
||||
*/
|
||||
EHCACHE,
|
||||
|
||||
/**
|
||||
* Hazelcast backed caching.
|
||||
*/
|
||||
HAZELCAST,
|
||||
|
||||
/**
|
||||
* Infinispan backed caching.
|
||||
*/
|
||||
INFINISPAN,
|
||||
|
||||
/**
|
||||
* Couchbase backed caching.
|
||||
*/
|
||||
COUCHBASE,
|
||||
|
||||
/**
|
||||
* Redis backed caching.
|
||||
*/
|
||||
REDIS,
|
||||
|
||||
/**
|
||||
* Caffeine backed caching.
|
||||
*/
|
||||
CAFFEINE,
|
||||
|
||||
/**
|
||||
* Simple in-memory caching.
|
||||
*/
|
||||
SIMPLE,
|
||||
|
||||
/**
|
||||
* No caching.
|
||||
*/
|
||||
NONE
|
||||
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.CaffeineSpec;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.caffeine.CaffeineCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Caffeine cache configuration.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Caffeine.class, CaffeineCacheManager.class })
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@Conditional({ CacheCondition.class })
|
||||
class CaffeineCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
private final Caffeine<Object, Object> caffeine;
|
||||
|
||||
private final CaffeineSpec caffeineSpec;
|
||||
|
||||
private final CacheLoader<Object, Object> cacheLoader;
|
||||
|
||||
CaffeineCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizers,
|
||||
ObjectProvider<Caffeine<Object, Object>> caffeine,
|
||||
ObjectProvider<CaffeineSpec> caffeineSpec,
|
||||
ObjectProvider<CacheLoader<Object, Object>> cacheLoader) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizers = customizers;
|
||||
this.caffeine = caffeine.getIfAvailable();
|
||||
this.caffeineSpec = caffeineSpec.getIfAvailable();
|
||||
this.cacheLoader = cacheLoader.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CaffeineCacheManager cacheManager() {
|
||||
CaffeineCacheManager cacheManager = createCacheManager();
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
if (!CollectionUtils.isEmpty(cacheNames)) {
|
||||
cacheManager.setCacheNames(cacheNames);
|
||||
}
|
||||
return this.customizers.customize(cacheManager);
|
||||
}
|
||||
|
||||
private CaffeineCacheManager createCacheManager() {
|
||||
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
|
||||
setCacheBuilder(cacheManager);
|
||||
if (this.cacheLoader != null) {
|
||||
cacheManager.setCacheLoader(this.cacheLoader);
|
||||
}
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
private void setCacheBuilder(CaffeineCacheManager cacheManager) {
|
||||
String specification = this.cacheProperties.getCaffeine().getSpec();
|
||||
if (StringUtils.hasText(specification)) {
|
||||
cacheManager.setCacheSpecification(specification);
|
||||
}
|
||||
else if (this.caffeineSpec != null) {
|
||||
cacheManager.setCaffeineSpec(this.caffeineSpec);
|
||||
}
|
||||
else if (this.caffeine != null) {
|
||||
cacheManager.setCaffeine(this.caffeine);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.couchbase.client.java.Bucket;
|
||||
import com.couchbase.client.spring.cache.CacheBuilder;
|
||||
import com.couchbase.client.spring.cache.CouchbaseCacheManager;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Couchbase cache configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.4.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Bucket.class, CouchbaseCacheManager.class })
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@ConditionalOnSingleCandidate(Bucket.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
public class CouchbaseCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
private final Bucket bucket;
|
||||
|
||||
public CouchbaseCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizers, Bucket bucket) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizers = customizers;
|
||||
this.bucket = bucket;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public CouchbaseCacheManager cacheManager() {
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
CouchbaseCacheManager cacheManager = new CouchbaseCacheManager(
|
||||
CacheBuilder.newInstance(this.bucket)
|
||||
.withExpiration(this.cacheProperties.getCouchbase()
|
||||
.getExpirationSeconds()),
|
||||
cacheNames.toArray(new String[cacheNames.size()]));
|
||||
return this.customizers.customize(cacheManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import net.sf.ehcache.Cache;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ResourceCondition;
|
||||
import org.springframework.cache.ehcache.EhCacheCacheManager;
|
||||
import org.springframework.cache.ehcache.EhCacheManagerUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* EhCache cache configuration. Only kick in if a configuration file location is set or if
|
||||
* a default configuration file exists.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Cache.class, EhCacheCacheManager.class })
|
||||
@ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
|
||||
@Conditional({ CacheCondition.class,
|
||||
EhCacheCacheConfiguration.ConfigAvailableCondition.class })
|
||||
class EhCacheCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
EhCacheCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizers) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizers = customizers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager) {
|
||||
return this.customizers.customize(new EhCacheCacheManager(ehCacheCacheManager));
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CacheManager ehCacheCacheManager() {
|
||||
Resource location = this.cacheProperties
|
||||
.resolveConfigLocation(this.cacheProperties.getEhcache().getConfig());
|
||||
if (location != null) {
|
||||
return EhCacheManagerUtils.buildCacheManager(location);
|
||||
}
|
||||
return EhCacheManagerUtils.buildCacheManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the EhCache configuration is available. This either kick in if a
|
||||
* default configuration has been found or if property referring to the file to use
|
||||
* has been set.
|
||||
*/
|
||||
static class ConfigAvailableCondition extends ResourceCondition {
|
||||
|
||||
ConfigAvailableCondition() {
|
||||
super("EhCache", "spring.cache.ehcache.config", "classpath:/ehcache.xml");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.support.SimpleCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Generic cache configuration based on arbitrary {@link Cache} instances defined in the
|
||||
* context.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean(Cache.class)
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
class GenericCacheConfiguration {
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
GenericCacheConfiguration(CacheManagerCustomizers customizers) {
|
||||
this.customizers = customizers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleCacheManager cacheManager(Collection<Cache> caches) {
|
||||
SimpleCacheManager cacheManager = new SimpleCacheManager();
|
||||
cacheManager.setCaches(caches);
|
||||
return this.customizers.customize(cacheManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.hazelcast.spring.cache.HazelcastCacheManager;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.hazelcast.HazelcastConfigResourceCondition;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Hazelcast cache configuration. Can either reuse the {@link HazelcastInstance} that has
|
||||
* been configured by the general {@link HazelcastAutoConfiguration} or create a separate
|
||||
* one if the {@code spring.cache.hazelcast.config} property has been set.
|
||||
* <p>
|
||||
* If the {@link HazelcastAutoConfiguration} has been disabled, an attempt to configure a
|
||||
* default {@link HazelcastInstance} is still made, using the same defaults.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
* @see HazelcastConfigResourceCondition
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ HazelcastInstance.class, HazelcastCacheManager.class })
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
@ConditionalOnSingleCandidate(HazelcastInstance.class)
|
||||
class HazelcastCacheConfiguration {
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
HazelcastCacheConfiguration(CacheManagerCustomizers customizers) {
|
||||
this.customizers = customizers;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HazelcastCacheManager cacheManager(HazelcastInstance existingHazelcastInstance)
|
||||
throws IOException {
|
||||
HazelcastCacheManager cacheManager = new HazelcastCacheManager(
|
||||
existingHazelcastInstance);
|
||||
return this.customizers.customize(cacheManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Properties;
|
||||
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
/**
|
||||
* JCache customization for Hazelcast.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(HazelcastInstance.class)
|
||||
class HazelcastJCacheCustomizationConfiguration {
|
||||
|
||||
@Bean
|
||||
public HazelcastPropertiesCustomizer hazelcastPropertiesCustomizer(
|
||||
ObjectProvider<HazelcastInstance> hazelcastInstance) {
|
||||
return new HazelcastPropertiesCustomizer(hazelcastInstance.getIfUnique());
|
||||
}
|
||||
|
||||
private static class HazelcastPropertiesCustomizer
|
||||
implements JCachePropertiesCustomizer {
|
||||
|
||||
private final HazelcastInstance hazelcastInstance;
|
||||
|
||||
HazelcastPropertiesCustomizer(HazelcastInstance hazelcastInstance) {
|
||||
this.hazelcastInstance = hazelcastInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(CacheProperties cacheProperties, Properties properties) {
|
||||
Resource configLocation = cacheProperties
|
||||
.resolveConfigLocation(cacheProperties.getJcache().getConfig());
|
||||
if (configLocation != null) {
|
||||
// Hazelcast does not use the URI as a mean to specify a custom config.
|
||||
properties.setProperty("hazelcast.config.location",
|
||||
toUri(configLocation).toString());
|
||||
}
|
||||
else if (this.hazelcastInstance != null) {
|
||||
properties.put("hazelcast.instance.itself", this.hazelcastInstance);
|
||||
}
|
||||
}
|
||||
|
||||
private static URI toUri(Resource config) {
|
||||
try {
|
||||
return config.getURI();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalArgumentException("Could not get URI from " + config,
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||
import org.infinispan.manager.DefaultCacheManager;
|
||||
import org.infinispan.manager.EmbeddedCacheManager;
|
||||
import org.infinispan.spring.provider.SpringEmbeddedCacheManager;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Infinispan cache configuration.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @author Raja Kolli
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass(SpringEmbeddedCacheManager.class)
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
public class InfinispanCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
private final ConfigurationBuilder defaultConfigurationBuilder;
|
||||
|
||||
public InfinispanCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizers,
|
||||
ObjectProvider<ConfigurationBuilder> defaultConfigurationBuilder) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizers = customizers;
|
||||
this.defaultConfigurationBuilder = defaultConfigurationBuilder.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpringEmbeddedCacheManager cacheManager(
|
||||
EmbeddedCacheManager embeddedCacheManager) {
|
||||
SpringEmbeddedCacheManager cacheManager = new SpringEmbeddedCacheManager(
|
||||
embeddedCacheManager);
|
||||
return this.customizers.customize(cacheManager);
|
||||
}
|
||||
|
||||
@Bean(destroyMethod = "stop")
|
||||
@ConditionalOnMissingBean
|
||||
public EmbeddedCacheManager infinispanCacheManager() throws IOException {
|
||||
EmbeddedCacheManager cacheManager = createEmbeddedCacheManager();
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
if (!CollectionUtils.isEmpty(cacheNames)) {
|
||||
for (String cacheName : cacheNames) {
|
||||
cacheManager.defineConfiguration(cacheName,
|
||||
getDefaultCacheConfiguration());
|
||||
}
|
||||
}
|
||||
return cacheManager;
|
||||
}
|
||||
|
||||
private EmbeddedCacheManager createEmbeddedCacheManager() throws IOException {
|
||||
Resource location = this.cacheProperties
|
||||
.resolveConfigLocation(this.cacheProperties.getInfinispan().getConfig());
|
||||
if (location != null) {
|
||||
try (InputStream in = location.getInputStream()) {
|
||||
return new DefaultCacheManager(in);
|
||||
}
|
||||
}
|
||||
return new DefaultCacheManager();
|
||||
}
|
||||
|
||||
private org.infinispan.configuration.cache.Configuration getDefaultCacheConfiguration() {
|
||||
if (this.defaultConfigurationBuilder != null) {
|
||||
return this.defaultConfigurationBuilder.build();
|
||||
}
|
||||
return new ConfigurationBuilder().build();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.cache.CacheManager;
|
||||
import javax.cache.Caching;
|
||||
import javax.cache.configuration.MutableConfiguration;
|
||||
import javax.cache.spi.CachingProvider;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
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.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
import org.springframework.cache.jcache.JCacheCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Cache configuration for JSR-107 compliant providers.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Madhura Bhave
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Caching.class, JCacheCacheManager.class })
|
||||
@ConditionalOnMissingBean(org.springframework.cache.CacheManager.class)
|
||||
@Conditional({ CacheCondition.class,
|
||||
JCacheCacheConfiguration.JCacheAvailableCondition.class })
|
||||
@Import(HazelcastJCacheCustomizationConfiguration.class)
|
||||
class JCacheCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizers;
|
||||
|
||||
private final javax.cache.configuration.Configuration<?, ?> defaultCacheConfiguration;
|
||||
|
||||
private final List<JCacheManagerCustomizer> cacheManagerCustomizers;
|
||||
|
||||
private final List<JCachePropertiesCustomizer> cachePropertiesCustomizers;
|
||||
|
||||
JCacheCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizers,
|
||||
ObjectProvider<javax.cache.configuration.Configuration<?, ?>> defaultCacheConfiguration,
|
||||
ObjectProvider<List<JCacheManagerCustomizer>> cacheManagerCustomizers,
|
||||
ObjectProvider<List<JCachePropertiesCustomizer>> cachePropertiesCustomizers) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizers = customizers;
|
||||
this.defaultCacheConfiguration = defaultCacheConfiguration.getIfAvailable();
|
||||
this.cacheManagerCustomizers = cacheManagerCustomizers.getIfAvailable();
|
||||
this.cachePropertiesCustomizers = cachePropertiesCustomizers.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JCacheCacheManager cacheManager(CacheManager jCacheCacheManager) {
|
||||
JCacheCacheManager cacheManager = new JCacheCacheManager(jCacheCacheManager);
|
||||
return this.customizers.customize(cacheManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CacheManager jCacheCacheManager() throws IOException {
|
||||
CacheManager jCacheCacheManager = createCacheManager();
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
if (!CollectionUtils.isEmpty(cacheNames)) {
|
||||
for (String cacheName : cacheNames) {
|
||||
jCacheCacheManager.createCache(cacheName, getDefaultCacheConfiguration());
|
||||
}
|
||||
}
|
||||
customize(jCacheCacheManager);
|
||||
return jCacheCacheManager;
|
||||
}
|
||||
|
||||
private CacheManager createCacheManager() throws IOException {
|
||||
CachingProvider cachingProvider = getCachingProvider(
|
||||
this.cacheProperties.getJcache().getProvider());
|
||||
Properties properties = createCacheManagerProperties();
|
||||
Resource configLocation = this.cacheProperties
|
||||
.resolveConfigLocation(this.cacheProperties.getJcache().getConfig());
|
||||
if (configLocation != null) {
|
||||
return cachingProvider.getCacheManager(configLocation.getURI(),
|
||||
cachingProvider.getDefaultClassLoader(), properties);
|
||||
}
|
||||
return cachingProvider.getCacheManager(null, null, properties);
|
||||
}
|
||||
|
||||
private CachingProvider getCachingProvider(String cachingProviderFqn) {
|
||||
if (StringUtils.hasText(cachingProviderFqn)) {
|
||||
return Caching.getCachingProvider(cachingProviderFqn);
|
||||
}
|
||||
return Caching.getCachingProvider();
|
||||
}
|
||||
|
||||
private Properties createCacheManagerProperties() {
|
||||
Properties properties = new Properties();
|
||||
if (this.cachePropertiesCustomizers != null) {
|
||||
for (JCachePropertiesCustomizer customizer : this.cachePropertiesCustomizers) {
|
||||
customizer.customize(this.cacheProperties, properties);
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
private javax.cache.configuration.Configuration<?, ?> getDefaultCacheConfiguration() {
|
||||
if (this.defaultCacheConfiguration != null) {
|
||||
return this.defaultCacheConfiguration;
|
||||
}
|
||||
return new MutableConfiguration<>();
|
||||
}
|
||||
|
||||
private void customize(CacheManager cacheManager) {
|
||||
if (this.cacheManagerCustomizers != null) {
|
||||
AnnotationAwareOrderComparator.sort(this.cacheManagerCustomizers);
|
||||
for (JCacheManagerCustomizer customizer : this.cacheManagerCustomizers) {
|
||||
customizer.customize(cacheManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if JCache is available. This either kicks in if a provider is available
|
||||
* as defined per {@link JCacheProviderAvailableCondition} or if a
|
||||
* {@link CacheManager} has already been defined.
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
static class JCacheAvailableCondition extends AnyNestedCondition {
|
||||
|
||||
JCacheAvailableCondition() {
|
||||
super(ConfigurationPhase.REGISTER_BEAN);
|
||||
}
|
||||
|
||||
@Conditional(JCacheProviderAvailableCondition.class)
|
||||
static class JCacheProvider {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnSingleCandidate(CacheManager.class)
|
||||
static class CustomJCacheCacheManager {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a JCache provider is available. This either kicks in if a default
|
||||
* {@link CachingProvider} has been found or if the property referring to the provider
|
||||
* to use has been set.
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
static class JCacheProviderAvailableCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ConditionMessage.Builder message = ConditionMessage.forCondition("JCache");
|
||||
String providerProperty = "spring.cache.jcache.provider";
|
||||
if (context.getEnvironment().containsProperty(providerProperty)) {
|
||||
return ConditionOutcome
|
||||
.match(message.because("JCache provider specified"));
|
||||
}
|
||||
Iterator<CachingProvider> providers = Caching.getCachingProviders()
|
||||
.iterator();
|
||||
if (!providers.hasNext()) {
|
||||
return ConditionOutcome
|
||||
.noMatch(message.didNotFind("JSR-107 provider").atAll());
|
||||
}
|
||||
providers.next();
|
||||
if (providers.hasNext()) {
|
||||
return ConditionOutcome
|
||||
.noMatch(message.foundExactly("multiple JSR-107 providers"));
|
||||
|
||||
}
|
||||
return ConditionOutcome
|
||||
.match(message.foundExactly("single JSR-107 provider"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import javax.cache.CacheManager;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize the cache
|
||||
* manager before it is used, in particular to create additional caches.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface JCacheManagerCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the cache manager.
|
||||
* @param cacheManager the {@code javax.cache.CacheManager} to customize
|
||||
*/
|
||||
void customize(CacheManager cacheManager);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.cache.CacheManager;
|
||||
import javax.cache.spi.CachingProvider;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize the properties
|
||||
* used by the {@link CachingProvider} to create the {@link CacheManager}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
interface JCachePropertiesCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the properties.
|
||||
* @param cacheProperties the cache properties
|
||||
* @param properties the current properties
|
||||
* @see CachingProvider#getCacheManager(java.net.URI, ClassLoader, Properties)
|
||||
*/
|
||||
void customize(CacheProperties cacheProperties, Properties properties);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.cache;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.support.NoOpCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* No-op cache configuration used to disable caching via configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
class NoOpCacheConfiguration {
|
||||
|
||||
@Bean
|
||||
public NoOpCacheManager cacheManager() {
|
||||
return new NoOpCacheManager();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cache;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||
import org.springframework.data.redis.cache.RedisCacheManager.RedisCacheManagerBuilder;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
|
||||
/**
|
||||
* Redis cache configuration.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Mark Paluch
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfigureAfter(RedisAutoConfiguration.class)
|
||||
@ConditionalOnBean(RedisConnectionFactory.class)
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
class RedisCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizerInvoker;
|
||||
|
||||
RedisCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizerInvoker) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizerInvoker = customizerInvoker;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
|
||||
RedisCacheManagerBuilder builder = RedisCacheManager
|
||||
.builder(redisConnectionFactory);
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
if (!cacheNames.isEmpty()) {
|
||||
builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
|
||||
}
|
||||
return this.customizerInvoker.customize(builder.build());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.cache;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Simplest cache configuration, usually used as a fallback.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnMissingBean(CacheManager.class)
|
||||
@Conditional(CacheCondition.class)
|
||||
class SimpleCacheConfiguration {
|
||||
|
||||
private final CacheProperties cacheProperties;
|
||||
|
||||
private final CacheManagerCustomizers customizerInvoker;
|
||||
|
||||
SimpleCacheConfiguration(CacheProperties cacheProperties,
|
||||
CacheManagerCustomizers customizerInvoker) {
|
||||
this.cacheProperties = cacheProperties;
|
||||
this.customizerInvoker = customizerInvoker;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ConcurrentMapCacheManager cacheManager() {
|
||||
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
|
||||
List<String> cacheNames = this.cacheProperties.getCacheNames();
|
||||
if (!cacheNames.isEmpty()) {
|
||||
cacheManager.setCacheNames(cacheNames);
|
||||
}
|
||||
return this.customizerInvoker.customize(cacheManager);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for the cache abstraction.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.cache;
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.PoolingOptions;
|
||||
import com.datastax.driver.core.QueryOptions;
|
||||
import com.datastax.driver.core.SocketOptions;
|
||||
import com.datastax.driver.core.policies.LoadBalancingPolicy;
|
||||
import com.datastax.driver.core.policies.ReconnectionPolicy;
|
||||
import com.datastax.driver.core.policies.RetryPolicy;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Cassandra.
|
||||
*
|
||||
* @author Julien Dubois
|
||||
* @author Phillip Webb
|
||||
* @author Eddú Meléndez
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnClass({ Cluster.class })
|
||||
@EnableConfigurationProperties(CassandraProperties.class)
|
||||
public class CassandraAutoConfiguration {
|
||||
|
||||
private final CassandraProperties properties;
|
||||
|
||||
private final List<ClusterBuilderCustomizer> builderCustomizers;
|
||||
|
||||
public CassandraAutoConfiguration(CassandraProperties properties,
|
||||
ObjectProvider<List<ClusterBuilderCustomizer>> builderCustomizers) {
|
||||
this.properties = properties;
|
||||
this.builderCustomizers = builderCustomizers.getIfAvailable();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public Cluster cassandraCluster() {
|
||||
CassandraProperties properties = this.properties;
|
||||
Cluster.Builder builder = Cluster.builder()
|
||||
.withClusterName(properties.getClusterName())
|
||||
.withPort(properties.getPort());
|
||||
if (properties.getUsername() != null) {
|
||||
builder.withCredentials(properties.getUsername(), properties.getPassword());
|
||||
}
|
||||
if (properties.getCompression() != null) {
|
||||
builder.withCompression(properties.getCompression());
|
||||
}
|
||||
if (properties.getLoadBalancingPolicy() != null) {
|
||||
LoadBalancingPolicy policy = instantiate(properties.getLoadBalancingPolicy());
|
||||
builder.withLoadBalancingPolicy(policy);
|
||||
}
|
||||
builder.withQueryOptions(getQueryOptions());
|
||||
if (properties.getReconnectionPolicy() != null) {
|
||||
ReconnectionPolicy policy = instantiate(properties.getReconnectionPolicy());
|
||||
builder.withReconnectionPolicy(policy);
|
||||
}
|
||||
if (properties.getRetryPolicy() != null) {
|
||||
RetryPolicy policy = instantiate(properties.getRetryPolicy());
|
||||
builder.withRetryPolicy(policy);
|
||||
}
|
||||
builder.withSocketOptions(getSocketOptions());
|
||||
if (properties.isSsl()) {
|
||||
builder.withSSL();
|
||||
}
|
||||
builder.withPoolingOptions(getPoolingOptions());
|
||||
String points = properties.getContactPoints();
|
||||
builder.addContactPoints(StringUtils.commaDelimitedListToStringArray(points));
|
||||
|
||||
customize(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private void customize(Cluster.Builder builder) {
|
||||
if (this.builderCustomizers != null) {
|
||||
for (ClusterBuilderCustomizer customizer : this.builderCustomizers) {
|
||||
customizer.customize(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T instantiate(Class<T> type) {
|
||||
return BeanUtils.instantiateClass(type);
|
||||
}
|
||||
|
||||
private QueryOptions getQueryOptions() {
|
||||
QueryOptions options = new QueryOptions();
|
||||
CassandraProperties properties = this.properties;
|
||||
if (properties.getConsistencyLevel() != null) {
|
||||
options.setConsistencyLevel(properties.getConsistencyLevel());
|
||||
}
|
||||
if (properties.getSerialConsistencyLevel() != null) {
|
||||
options.setSerialConsistencyLevel(properties.getSerialConsistencyLevel());
|
||||
}
|
||||
options.setFetchSize(properties.getFetchSize());
|
||||
return options;
|
||||
}
|
||||
|
||||
private SocketOptions getSocketOptions() {
|
||||
SocketOptions options = new SocketOptions();
|
||||
options.setConnectTimeoutMillis(this.properties.getConnectTimeoutMillis());
|
||||
options.setReadTimeoutMillis(this.properties.getReadTimeoutMillis());
|
||||
return options;
|
||||
}
|
||||
|
||||
private PoolingOptions getPoolingOptions() {
|
||||
CassandraProperties.Pool pool = this.properties.getPool();
|
||||
PoolingOptions options = new PoolingOptions();
|
||||
options.setIdleTimeoutSeconds(pool.getIdleTimeout());
|
||||
options.setPoolTimeoutMillis(pool.getPoolTimeout());
|
||||
options.setHeartbeatIntervalSeconds(pool.getHeartbeatInterval());
|
||||
options.setMaxQueueSize(pool.getMaxQueueSize());
|
||||
return options;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import com.datastax.driver.core.ConsistencyLevel;
|
||||
import com.datastax.driver.core.ProtocolOptions;
|
||||
import com.datastax.driver.core.ProtocolOptions.Compression;
|
||||
import com.datastax.driver.core.QueryOptions;
|
||||
import com.datastax.driver.core.SocketOptions;
|
||||
import com.datastax.driver.core.policies.LoadBalancingPolicy;
|
||||
import com.datastax.driver.core.policies.ReconnectionPolicy;
|
||||
import com.datastax.driver.core.policies.RetryPolicy;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for Cassandra.
|
||||
*
|
||||
* @author Julien Dubois
|
||||
* @author Phillip Webb
|
||||
* @author Mark Paluch
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@ConfigurationProperties(prefix = "spring.data.cassandra")
|
||||
public class CassandraProperties {
|
||||
|
||||
/**
|
||||
* Keyspace name to use.
|
||||
*/
|
||||
private String keyspaceName;
|
||||
|
||||
/**
|
||||
* Name of the Cassandra cluster.
|
||||
*/
|
||||
private String clusterName;
|
||||
|
||||
/**
|
||||
* Comma-separated list of cluster node addresses.
|
||||
*/
|
||||
private String contactPoints = "localhost";
|
||||
|
||||
/**
|
||||
* Port of the Cassandra server.
|
||||
*/
|
||||
private int port = ProtocolOptions.DEFAULT_PORT;
|
||||
|
||||
/**
|
||||
* Login user of the server.
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* Login password of the server.
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* Compression supported by the Cassandra binary protocol.
|
||||
*/
|
||||
private Compression compression = Compression.NONE;
|
||||
|
||||
/**
|
||||
* Class name of the load balancing policy.
|
||||
*/
|
||||
private Class<? extends LoadBalancingPolicy> loadBalancingPolicy;
|
||||
|
||||
/**
|
||||
* Queries consistency level.
|
||||
*/
|
||||
private ConsistencyLevel consistencyLevel;
|
||||
|
||||
/**
|
||||
* Queries serial consistency level.
|
||||
*/
|
||||
private ConsistencyLevel serialConsistencyLevel;
|
||||
|
||||
/**
|
||||
* Queries default fetch size.
|
||||
*/
|
||||
private int fetchSize = QueryOptions.DEFAULT_FETCH_SIZE;
|
||||
|
||||
/**
|
||||
* Reconnection policy class.
|
||||
*/
|
||||
private Class<? extends ReconnectionPolicy> reconnectionPolicy;
|
||||
|
||||
/**
|
||||
* Class name of the retry policy.
|
||||
*/
|
||||
private Class<? extends RetryPolicy> retryPolicy;
|
||||
|
||||
/**
|
||||
* Socket option: connection time out.
|
||||
*/
|
||||
private int connectTimeoutMillis = SocketOptions.DEFAULT_CONNECT_TIMEOUT_MILLIS;
|
||||
|
||||
/**
|
||||
* Socket option: read time out.
|
||||
*/
|
||||
private int readTimeoutMillis = SocketOptions.DEFAULT_READ_TIMEOUT_MILLIS;
|
||||
|
||||
/**
|
||||
* Schema action to take at startup.
|
||||
*/
|
||||
private String schemaAction = "none";
|
||||
|
||||
/**
|
||||
* Enable SSL support.
|
||||
*/
|
||||
private boolean ssl = false;
|
||||
|
||||
/**
|
||||
* Pool configuration.
|
||||
*/
|
||||
private final Pool pool = new Pool();
|
||||
|
||||
public String getKeyspaceName() {
|
||||
return this.keyspaceName;
|
||||
}
|
||||
|
||||
public void setKeyspaceName(String keyspaceName) {
|
||||
this.keyspaceName = keyspaceName;
|
||||
}
|
||||
|
||||
public String getClusterName() {
|
||||
return this.clusterName;
|
||||
}
|
||||
|
||||
public void setClusterName(String clusterName) {
|
||||
this.clusterName = clusterName;
|
||||
}
|
||||
|
||||
public String getContactPoints() {
|
||||
return this.contactPoints;
|
||||
}
|
||||
|
||||
public void setContactPoints(String contactPoints) {
|
||||
this.contactPoints = contactPoints;
|
||||
}
|
||||
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return this.username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return this.password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public Compression getCompression() {
|
||||
return this.compression;
|
||||
}
|
||||
|
||||
public void setCompression(Compression compression) {
|
||||
this.compression = compression;
|
||||
}
|
||||
|
||||
public Class<? extends LoadBalancingPolicy> getLoadBalancingPolicy() {
|
||||
return this.loadBalancingPolicy;
|
||||
}
|
||||
|
||||
public void setLoadBalancingPolicy(
|
||||
Class<? extends LoadBalancingPolicy> loadBalancingPolicy) {
|
||||
this.loadBalancingPolicy = loadBalancingPolicy;
|
||||
}
|
||||
|
||||
public ConsistencyLevel getConsistencyLevel() {
|
||||
return this.consistencyLevel;
|
||||
}
|
||||
|
||||
public void setConsistencyLevel(ConsistencyLevel consistency) {
|
||||
this.consistencyLevel = consistency;
|
||||
}
|
||||
|
||||
public ConsistencyLevel getSerialConsistencyLevel() {
|
||||
return this.serialConsistencyLevel;
|
||||
}
|
||||
|
||||
public void setSerialConsistencyLevel(ConsistencyLevel serialConsistency) {
|
||||
this.serialConsistencyLevel = serialConsistency;
|
||||
}
|
||||
|
||||
public int getFetchSize() {
|
||||
return this.fetchSize;
|
||||
}
|
||||
|
||||
public void setFetchSize(int fetchSize) {
|
||||
this.fetchSize = fetchSize;
|
||||
}
|
||||
|
||||
public Class<? extends ReconnectionPolicy> getReconnectionPolicy() {
|
||||
return this.reconnectionPolicy;
|
||||
}
|
||||
|
||||
public void setReconnectionPolicy(
|
||||
Class<? extends ReconnectionPolicy> reconnectionPolicy) {
|
||||
this.reconnectionPolicy = reconnectionPolicy;
|
||||
}
|
||||
|
||||
public Class<? extends RetryPolicy> getRetryPolicy() {
|
||||
return this.retryPolicy;
|
||||
}
|
||||
|
||||
public void setRetryPolicy(Class<? extends RetryPolicy> retryPolicy) {
|
||||
this.retryPolicy = retryPolicy;
|
||||
}
|
||||
|
||||
public int getConnectTimeoutMillis() {
|
||||
return this.connectTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setConnectTimeoutMillis(int connectTimeoutMillis) {
|
||||
this.connectTimeoutMillis = connectTimeoutMillis;
|
||||
}
|
||||
|
||||
public int getReadTimeoutMillis() {
|
||||
return this.readTimeoutMillis;
|
||||
}
|
||||
|
||||
public void setReadTimeoutMillis(int readTimeoutMillis) {
|
||||
this.readTimeoutMillis = readTimeoutMillis;
|
||||
}
|
||||
|
||||
public boolean isSsl() {
|
||||
return this.ssl;
|
||||
}
|
||||
|
||||
public void setSsl(boolean ssl) {
|
||||
this.ssl = ssl;
|
||||
}
|
||||
|
||||
public String getSchemaAction() {
|
||||
return this.schemaAction;
|
||||
}
|
||||
|
||||
public void setSchemaAction(String schemaAction) {
|
||||
this.schemaAction = schemaAction;
|
||||
}
|
||||
|
||||
public Pool getPool() {
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pool properties.
|
||||
*/
|
||||
public static class Pool {
|
||||
|
||||
/**
|
||||
* Idle timeout (in seconds) before an idle connection is removed.
|
||||
*/
|
||||
private int idleTimeout = 120;
|
||||
|
||||
/**
|
||||
* Pool timeout (in milliseconds) when trying to acquire a connection from a
|
||||
* host's pool.
|
||||
*/
|
||||
private int poolTimeout = 5000;
|
||||
|
||||
/**
|
||||
* Heartbeat interval (in seconds) after which a message is sent on an idle
|
||||
* connection to make sure it's still alive.
|
||||
*/
|
||||
private int heartbeatInterval = 30;
|
||||
|
||||
/**
|
||||
* Maximum number of requests that get enqueued if no connection is available.
|
||||
*/
|
||||
private int maxQueueSize = 256;
|
||||
|
||||
public int getIdleTimeout() {
|
||||
return this.idleTimeout;
|
||||
}
|
||||
|
||||
public void setIdleTimeout(int idleTimeout) {
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
public int getPoolTimeout() {
|
||||
return this.poolTimeout;
|
||||
}
|
||||
|
||||
public void setPoolTimeout(int poolTimeout) {
|
||||
this.poolTimeout = poolTimeout;
|
||||
}
|
||||
|
||||
public int getHeartbeatInterval() {
|
||||
return this.heartbeatInterval;
|
||||
}
|
||||
|
||||
public void setHeartbeatInterval(int heartbeatInterval) {
|
||||
this.heartbeatInterval = heartbeatInterval;
|
||||
}
|
||||
|
||||
public int getMaxQueueSize() {
|
||||
return this.maxQueueSize;
|
||||
}
|
||||
|
||||
public void setMaxQueueSize(int maxQueueSize) {
|
||||
this.maxQueueSize = maxQueueSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.cassandra;
|
||||
|
||||
import com.datastax.driver.core.Cluster;
|
||||
import com.datastax.driver.core.Cluster.Builder;
|
||||
|
||||
/**
|
||||
* Callback interface that can be implemented by beans wishing to customize the
|
||||
* {@link Cluster} via a {@link Builder Cluster.Builder} whilst retaining default
|
||||
* auto-configuration.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ClusterBuilderCustomizer {
|
||||
|
||||
/**
|
||||
* Customize the {@link Builder}.
|
||||
* @param clusterBuilder the builder to customize
|
||||
*/
|
||||
void customize(Builder clusterBuilder);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Cassandra.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.cassandra;
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.cloud;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.Cloud;
|
||||
import org.springframework.cloud.app.ApplicationInstanceInfo;
|
||||
import org.springframework.cloud.config.java.CloudScan;
|
||||
import org.springframework.cloud.config.java.CloudScanConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for Spring Cloud.
|
||||
* <p>
|
||||
* Activates when there is no bean of type {@link Cloud} is configured in the context, the
|
||||
* {@link Cloud} type (this spring-cloud) is on the classpath, and the "cloud" profile is
|
||||
* active.
|
||||
* <p>
|
||||
* Once in effect, the auto-configuration is the equivalent of adding the
|
||||
* {@link CloudScan} annotation in one of the configuration file. Specifically, it adds a
|
||||
* bean for each service bound to the application and one for
|
||||
* {@link ApplicationInstanceInfo}
|
||||
*
|
||||
* @author Ramnivas Laddad
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("cloud")
|
||||
@AutoConfigureOrder(CloudAutoConfiguration.ORDER)
|
||||
@ConditionalOnClass(CloudScanConfiguration.class)
|
||||
@ConditionalOnMissingBean(Cloud.class)
|
||||
@ConditionalOnProperty(prefix = "spring.cloud", name = "enabled", havingValue = "true", matchIfMissing = true)
|
||||
@Import(CloudScanConfiguration.class)
|
||||
public class CloudAutoConfiguration {
|
||||
|
||||
// Cloud configuration needs to happen early (before data, mongo etc.)
|
||||
public static final int ORDER = Ordered.HIGHEST_PRECEDENCE + 20;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for Spring Cloud Connectors.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.cloud;
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.ConfigurationCondition;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Abstract base class for nested conditions.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
abstract class AbstractNestedCondition extends SpringBootCondition
|
||||
implements ConfigurationCondition {
|
||||
|
||||
private final ConfigurationPhase configurationPhase;
|
||||
|
||||
AbstractNestedCondition(ConfigurationPhase configurationPhase) {
|
||||
Assert.notNull(configurationPhase, "ConfigurationPhase must not be null");
|
||||
this.configurationPhase = configurationPhase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationPhase getConfigurationPhase() {
|
||||
return this.configurationPhase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
String className = getClass().getName();
|
||||
MemberConditions memberConditions = new MemberConditions(context, className);
|
||||
MemberMatchOutcomes memberOutcomes = new MemberMatchOutcomes(memberConditions);
|
||||
return getFinalMatchOutcome(memberOutcomes);
|
||||
}
|
||||
|
||||
protected abstract ConditionOutcome getFinalMatchOutcome(
|
||||
MemberMatchOutcomes memberOutcomes);
|
||||
|
||||
protected static class MemberMatchOutcomes {
|
||||
|
||||
private final List<ConditionOutcome> all;
|
||||
|
||||
private final List<ConditionOutcome> matches;
|
||||
|
||||
private final List<ConditionOutcome> nonMatches;
|
||||
|
||||
public MemberMatchOutcomes(MemberConditions memberConditions) {
|
||||
this.all = Collections.unmodifiableList(memberConditions.getMatchOutcomes());
|
||||
List<ConditionOutcome> matches = new ArrayList<>();
|
||||
List<ConditionOutcome> nonMatches = new ArrayList<>();
|
||||
for (ConditionOutcome outcome : this.all) {
|
||||
(outcome.isMatch() ? matches : nonMatches).add(outcome);
|
||||
}
|
||||
this.matches = Collections.unmodifiableList(matches);
|
||||
this.nonMatches = Collections.unmodifiableList(nonMatches);
|
||||
}
|
||||
|
||||
public List<ConditionOutcome> getAll() {
|
||||
return this.all;
|
||||
}
|
||||
|
||||
public List<ConditionOutcome> getMatches() {
|
||||
return this.matches;
|
||||
}
|
||||
|
||||
public List<ConditionOutcome> getNonMatches() {
|
||||
return this.nonMatches;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MemberConditions {
|
||||
|
||||
private final ConditionContext context;
|
||||
|
||||
private final MetadataReaderFactory readerFactory;
|
||||
|
||||
private final Map<AnnotationMetadata, List<Condition>> memberConditions;
|
||||
|
||||
MemberConditions(ConditionContext context, String className) {
|
||||
this.context = context;
|
||||
this.readerFactory = new SimpleMetadataReaderFactory(
|
||||
context.getResourceLoader());
|
||||
String[] members = getMetadata(className).getMemberClassNames();
|
||||
this.memberConditions = getMemberConditions(members);
|
||||
}
|
||||
|
||||
private Map<AnnotationMetadata, List<Condition>> getMemberConditions(
|
||||
String[] members) {
|
||||
MultiValueMap<AnnotationMetadata, Condition> memberConditions = new LinkedMultiValueMap<>();
|
||||
for (String member : members) {
|
||||
AnnotationMetadata metadata = getMetadata(member);
|
||||
for (String[] conditionClasses : getConditionClasses(metadata)) {
|
||||
for (String conditionClass : conditionClasses) {
|
||||
Condition condition = getCondition(conditionClass);
|
||||
memberConditions.add(metadata, condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(memberConditions);
|
||||
}
|
||||
|
||||
private AnnotationMetadata getMetadata(String className) {
|
||||
try {
|
||||
return this.readerFactory.getMetadataReader(className)
|
||||
.getAnnotationMetadata();
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<String[]> getConditionClasses(AnnotatedTypeMetadata metadata) {
|
||||
MultiValueMap<String, Object> attributes = metadata
|
||||
.getAllAnnotationAttributes(Conditional.class.getName(), true);
|
||||
Object values = (attributes != null ? attributes.get("value") : null);
|
||||
return (List<String[]>) (values != null ? values : Collections.emptyList());
|
||||
}
|
||||
|
||||
private Condition getCondition(String conditionClassName) {
|
||||
Class<?> conditionClass = ClassUtils.resolveClassName(conditionClassName,
|
||||
this.context.getClassLoader());
|
||||
return (Condition) BeanUtils.instantiateClass(conditionClass);
|
||||
}
|
||||
|
||||
public List<ConditionOutcome> getMatchOutcomes() {
|
||||
List<ConditionOutcome> outcomes = new ArrayList<>();
|
||||
for (Map.Entry<AnnotationMetadata, List<Condition>> entry : this.memberConditions
|
||||
.entrySet()) {
|
||||
AnnotationMetadata metadata = entry.getKey();
|
||||
List<Condition> conditions = entry.getValue();
|
||||
outcomes.add(new MemberOutcomes(this.context, metadata, conditions)
|
||||
.getUltimateOutcome());
|
||||
}
|
||||
return Collections.unmodifiableList(outcomes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MemberOutcomes {
|
||||
|
||||
private final ConditionContext context;
|
||||
|
||||
private final AnnotationMetadata metadata;
|
||||
|
||||
private final List<ConditionOutcome> outcomes;
|
||||
|
||||
MemberOutcomes(ConditionContext context, AnnotationMetadata metadata,
|
||||
List<Condition> conditions) {
|
||||
this.context = context;
|
||||
this.metadata = metadata;
|
||||
this.outcomes = new ArrayList<>(conditions.size());
|
||||
for (Condition condition : conditions) {
|
||||
this.outcomes.add(getConditionOutcome(metadata, condition));
|
||||
}
|
||||
}
|
||||
|
||||
private ConditionOutcome getConditionOutcome(AnnotationMetadata metadata,
|
||||
Condition condition) {
|
||||
if (condition instanceof SpringBootCondition) {
|
||||
return ((SpringBootCondition) condition).getMatchOutcome(this.context,
|
||||
metadata);
|
||||
}
|
||||
return new ConditionOutcome(condition.matches(this.context, metadata),
|
||||
ConditionMessage.empty());
|
||||
}
|
||||
|
||||
public ConditionOutcome getUltimateOutcome() {
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition("NestedCondition on "
|
||||
+ ClassUtils.getShortName(this.metadata.getClassName()));
|
||||
if (this.outcomes.size() == 1) {
|
||||
ConditionOutcome outcome = this.outcomes.get(0);
|
||||
return new ConditionOutcome(outcome.isMatch(),
|
||||
message.because(outcome.getMessage()));
|
||||
}
|
||||
List<ConditionOutcome> match = new ArrayList<>();
|
||||
List<ConditionOutcome> nonMatch = new ArrayList<>();
|
||||
for (ConditionOutcome outcome : this.outcomes) {
|
||||
(outcome.isMatch() ? match : nonMatch).add(outcome);
|
||||
}
|
||||
if (nonMatch.isEmpty()) {
|
||||
return ConditionOutcome
|
||||
.match(message.found("matching nested conditions").items(match));
|
||||
}
|
||||
return ConditionOutcome.noMatch(
|
||||
message.found("non-matching nested conditions").items(nonMatch));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
|
||||
/**
|
||||
* {@link Condition} that will match when all nested class conditions match. Can be used
|
||||
* to create composite conditions, for example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* static class OnJndiAndProperty extends AllNestedConditions {
|
||||
*
|
||||
* OnJndiAndProperty() {
|
||||
* super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
* }
|
||||
*
|
||||
* @ConditionalOnJndi()
|
||||
* static class OnJndi {
|
||||
* }
|
||||
*
|
||||
* @ConditionalOnProperty("something")
|
||||
* static class OnProperty {
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The
|
||||
* {@link org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase
|
||||
* ConfigurationPhase} should be specified according to the conditions that are defined.
|
||||
* In the example above, all conditions are static and can be evaluated early so
|
||||
* {@code PARSE_CONFIGURATION} is a right fit.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public abstract class AllNestedConditions extends AbstractNestedCondition {
|
||||
|
||||
public AllNestedConditions(ConfigurationPhase configurationPhase) {
|
||||
super(configurationPhase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
|
||||
boolean match = hasSameSize(memberOutcomes.getMatches(), memberOutcomes.getAll());
|
||||
List<ConditionMessage> messages = new ArrayList<>();
|
||||
messages.add(ConditionMessage.forCondition("AllNestedConditions")
|
||||
.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));
|
||||
}
|
||||
|
||||
private boolean hasSameSize(List<?> list1, List<?> list2) {
|
||||
return list1.size() == list2.size();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
|
||||
/**
|
||||
* {@link Condition} that will match when any nested class condition matches. Can be used
|
||||
* to create composite conditions, for example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* static class OnJndiOrProperty extends AnyNestedCondition {
|
||||
*
|
||||
* OnJndiOrProperty() {
|
||||
* super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
* }
|
||||
*
|
||||
* @ConditionalOnJndi()
|
||||
* static class OnJndi {
|
||||
* }
|
||||
*
|
||||
* @ConditionalOnProperty("something")
|
||||
* static class OnProperty {
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The
|
||||
* {@link org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase
|
||||
* ConfigurationPhase} should be specified according to the conditions that are defined.
|
||||
* In the example above, all conditions are static and can be evaluated early so
|
||||
* {@code PARSE_CONFIGURATION} is a right fit.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 20)
|
||||
public abstract class AnyNestedCondition extends AbstractNestedCondition {
|
||||
|
||||
public AnyNestedCondition(ConfigurationPhase configurationPhase) {
|
||||
super(configurationPhase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
|
||||
boolean match = !memberOutcomes.getMatches().isEmpty();
|
||||
List<ConditionMessage> messages = new ArrayList<>();
|
||||
messages.add(ConditionMessage.forCondition("AnyNestedCondition")
|
||||
.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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.CannotLoadBeanClassException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.type.MethodMetadata;
|
||||
import org.springframework.core.type.StandardMethodMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A registry of the bean types that are contained in a
|
||||
* {@link DefaultListableBeanFactory}. Provides similar functionality to
|
||||
* {@link ListableBeanFactory#getBeanNamesForType(Class, boolean, boolean)} but is
|
||||
* optimized for use by {@link OnBeanCondition} based on the following assumptions:
|
||||
* <ul>
|
||||
* <li>Bean definitions will not change type.</li>
|
||||
* <li>Beans definitions will not be removed.</li>
|
||||
* <li>Beans will not be created in parallel.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.2.0
|
||||
*/
|
||||
final class BeanTypeRegistry implements SmartInitializingSingleton {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(BeanTypeRegistry.class);
|
||||
|
||||
static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
|
||||
|
||||
private static final String BEAN_NAME = BeanTypeRegistry.class.getName();
|
||||
|
||||
private final DefaultListableBeanFactory beanFactory;
|
||||
|
||||
private final Map<String, Class<?>> beanTypes = new HashMap<>();
|
||||
|
||||
private int lastBeanDefinitionCount = 0;
|
||||
|
||||
private BeanTypeRegistry(DefaultListableBeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to get the {@link BeanTypeRegistry} for a given {@link BeanFactory}.
|
||||
* @param beanFactory the source bean factory
|
||||
* @return the {@link BeanTypeRegistry} for the given bean factory
|
||||
*/
|
||||
static BeanTypeRegistry get(ListableBeanFactory beanFactory) {
|
||||
Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory);
|
||||
DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
|
||||
Assert.isTrue(listableBeanFactory.isAllowEagerClassLoading(),
|
||||
"Bean factory must allow eager class loading");
|
||||
if (!listableBeanFactory.containsLocalBean(BEAN_NAME)) {
|
||||
BeanDefinition bd = new RootBeanDefinition(BeanTypeRegistry.class);
|
||||
bd.getConstructorArgumentValues().addIndexedArgumentValue(0, beanFactory);
|
||||
listableBeanFactory.registerBeanDefinition(BEAN_NAME, bd);
|
||||
|
||||
}
|
||||
return listableBeanFactory.getBean(BEAN_NAME, BeanTypeRegistry.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the names of beans matching the given type (including subclasses), judging
|
||||
* from either bean definitions or the value of {@link FactoryBean#getObjectType()} in
|
||||
* the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not
|
||||
* cause early bean initialization.
|
||||
* @param type the class or interface to match (must not be {@code null})
|
||||
* @return the names of beans (or objects created by FactoryBeans) matching the given
|
||||
* object type (including subclasses), or an empty set if none
|
||||
*/
|
||||
Set<String> getNamesForType(Class<?> type) {
|
||||
updateTypesIfNecessary();
|
||||
Set<String> matches = new LinkedHashSet<>();
|
||||
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
|
||||
if (entry.getValue() != null && type.isAssignableFrom(entry.getValue())) {
|
||||
matches.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of beans annotated with the given {@code annotation}, judging
|
||||
* from either bean definitions or the value of {@link FactoryBean#getObjectType()} in
|
||||
* the case of {@link FactoryBean FactoryBeans}. Will include singletons but will not
|
||||
* cause early bean initialization.
|
||||
* @param annotation the annotation to match (must not be {@code null})
|
||||
* @return the names of beans (or objects created by FactoryBeans) annotated with the
|
||||
* given annotation, or an empty set if none
|
||||
*/
|
||||
Set<String> getNamesForAnnotation(Class<? extends Annotation> annotation) {
|
||||
updateTypesIfNecessary();
|
||||
Set<String> matches = new LinkedHashSet<>();
|
||||
for (Map.Entry<String, Class<?>> entry : this.beanTypes.entrySet()) {
|
||||
if (entry.getValue() != null && AnnotationUtils
|
||||
.findAnnotation(entry.getValue(), annotation) != null) {
|
||||
matches.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterSingletonsInstantiated() {
|
||||
// We're done at this point, free up some memory
|
||||
this.beanTypes.clear();
|
||||
this.lastBeanDefinitionCount = 0;
|
||||
}
|
||||
|
||||
private void addBeanType(String name) {
|
||||
if (this.beanFactory.containsSingleton(name)) {
|
||||
this.beanTypes.put(name, this.beanFactory.getType(name));
|
||||
}
|
||||
else if (!this.beanFactory.isAlias(name)) {
|
||||
addBeanTypeForNonAliasDefinition(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void addBeanTypeForNonAliasDefinition(String name) {
|
||||
try {
|
||||
String factoryName = BeanFactory.FACTORY_BEAN_PREFIX + name;
|
||||
RootBeanDefinition beanDefinition = (RootBeanDefinition) this.beanFactory
|
||||
.getMergedBeanDefinition(name);
|
||||
if (!beanDefinition.isAbstract()
|
||||
&& !requiresEagerInit(beanDefinition.getFactoryBeanName())) {
|
||||
if (this.beanFactory.isFactoryBean(factoryName)) {
|
||||
Class<?> factoryBeanGeneric = getFactoryBeanGeneric(this.beanFactory,
|
||||
beanDefinition, name);
|
||||
this.beanTypes.put(name, factoryBeanGeneric);
|
||||
this.beanTypes.put(factoryName,
|
||||
this.beanFactory.getType(factoryName));
|
||||
}
|
||||
else {
|
||||
this.beanTypes.put(name, this.beanFactory.getType(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CannotLoadBeanClassException ex) {
|
||||
// Probably contains a placeholder
|
||||
logIgnoredError("bean class loading failure for bean", name, ex);
|
||||
}
|
||||
catch (BeanDefinitionStoreException ex) {
|
||||
// Probably contains a placeholder
|
||||
logIgnoredError("unresolvable metadata in bean definition", name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void logIgnoredError(String message, String name, Exception ex) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring " + message + " '" + name + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean requiresEagerInit(String factoryBeanName) {
|
||||
return (factoryBeanName != null && this.beanFactory.isFactoryBean(factoryBeanName)
|
||||
&& !this.beanFactory.containsSingleton(factoryBeanName));
|
||||
}
|
||||
|
||||
private void updateTypesIfNecessary() {
|
||||
if (this.lastBeanDefinitionCount != this.beanFactory.getBeanDefinitionCount()) {
|
||||
Iterator<String> names = this.beanFactory.getBeanNamesIterator();
|
||||
while (names.hasNext()) {
|
||||
String name = names.next();
|
||||
if (!this.beanTypes.containsKey(name)) {
|
||||
addBeanType(name);
|
||||
}
|
||||
}
|
||||
this.lastBeanDefinitionCount = this.beanFactory.getBeanDefinitionCount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to guess the type that a {@link FactoryBean} will return based on the
|
||||
* generics in its method signature.
|
||||
* @param beanFactory the source bean factory
|
||||
* @param definition the bean definition
|
||||
* @param name the name of the bean
|
||||
* @return the generic type of the {@link FactoryBean} or {@code null}
|
||||
*/
|
||||
private Class<?> getFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinition definition, String name) {
|
||||
try {
|
||||
return doGetFactoryBeanGeneric(beanFactory, definition, name);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Class<?> doGetFactoryBeanGeneric(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinition definition, String name)
|
||||
throws Exception, ClassNotFoundException, LinkageError {
|
||||
if (StringUtils.hasLength(definition.getFactoryBeanName())
|
||||
&& StringUtils.hasLength(definition.getFactoryMethodName())) {
|
||||
return getConfigurationClassFactoryBeanGeneric(beanFactory, definition, name);
|
||||
}
|
||||
if (StringUtils.hasLength(definition.getBeanClassName())) {
|
||||
return getDirectFactoryBeanGeneric(beanFactory, definition, name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Class<?> getConfigurationClassFactoryBeanGeneric(
|
||||
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
|
||||
String name) throws Exception {
|
||||
Method method = getFactoryMethod(beanFactory, definition);
|
||||
Class<?> generic = ResolvableType.forMethodReturnType(method)
|
||||
.as(FactoryBean.class).resolveGeneric();
|
||||
if ((generic == null || generic.equals(Object.class))
|
||||
&& definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) {
|
||||
generic = getTypeFromAttribute(
|
||||
definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE));
|
||||
}
|
||||
return generic;
|
||||
}
|
||||
|
||||
private Method getFactoryMethod(ConfigurableListableBeanFactory beanFactory,
|
||||
BeanDefinition definition) throws Exception {
|
||||
if (definition instanceof AnnotatedBeanDefinition) {
|
||||
MethodMetadata factoryMethodMetadata = ((AnnotatedBeanDefinition) definition)
|
||||
.getFactoryMethodMetadata();
|
||||
if (factoryMethodMetadata instanceof StandardMethodMetadata) {
|
||||
return ((StandardMethodMetadata) factoryMethodMetadata)
|
||||
.getIntrospectedMethod();
|
||||
}
|
||||
}
|
||||
BeanDefinition factoryDefinition = beanFactory
|
||||
.getBeanDefinition(definition.getFactoryBeanName());
|
||||
Class<?> factoryClass = ClassUtils.forName(factoryDefinition.getBeanClassName(),
|
||||
beanFactory.getBeanClassLoader());
|
||||
return getFactoryMethod(definition, factoryClass);
|
||||
}
|
||||
|
||||
private Method getFactoryMethod(BeanDefinition definition, Class<?> factoryClass) {
|
||||
Method uniqueMethod = null;
|
||||
for (Method candidate : getCandidateFactoryMethods(definition, factoryClass)) {
|
||||
if (candidate.getName().equals(definition.getFactoryMethodName())) {
|
||||
if (uniqueMethod == null) {
|
||||
uniqueMethod = candidate;
|
||||
}
|
||||
else if (!hasMatchingParameterTypes(candidate, uniqueMethod)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return uniqueMethod;
|
||||
}
|
||||
|
||||
private Method[] getCandidateFactoryMethods(BeanDefinition definition,
|
||||
Class<?> factoryClass) {
|
||||
return shouldConsiderNonPublicMethods(definition)
|
||||
? ReflectionUtils.getAllDeclaredMethods(factoryClass)
|
||||
: factoryClass.getMethods();
|
||||
}
|
||||
|
||||
private boolean shouldConsiderNonPublicMethods(BeanDefinition definition) {
|
||||
return (definition instanceof AbstractBeanDefinition)
|
||||
&& ((AbstractBeanDefinition) definition).isNonPublicAccessAllowed();
|
||||
}
|
||||
|
||||
private boolean hasMatchingParameterTypes(Method candidate, Method current) {
|
||||
return Arrays.equals(candidate.getParameterTypes(), current.getParameterTypes());
|
||||
}
|
||||
|
||||
private Class<?> getDirectFactoryBeanGeneric(
|
||||
ConfigurableListableBeanFactory beanFactory, BeanDefinition definition,
|
||||
String name) throws ClassNotFoundException, LinkageError {
|
||||
Class<?> factoryBeanClass = ClassUtils.forName(definition.getBeanClassName(),
|
||||
beanFactory.getBeanClassLoader());
|
||||
Class<?> generic = ResolvableType.forClass(factoryBeanClass).as(FactoryBean.class)
|
||||
.resolveGeneric();
|
||||
if ((generic == null || generic.equals(Object.class))
|
||||
&& definition.hasAttribute(FACTORY_BEAN_OBJECT_TYPE)) {
|
||||
generic = getTypeFromAttribute(
|
||||
definition.getAttribute(FACTORY_BEAN_OBJECT_TYPE));
|
||||
}
|
||||
return generic;
|
||||
}
|
||||
|
||||
private Class<?> getTypeFromAttribute(Object attribute)
|
||||
throws ClassNotFoundException, LinkageError {
|
||||
if (attribute instanceof Class<?>) {
|
||||
return (Class<?>) attribute;
|
||||
}
|
||||
if (attribute instanceof String) {
|
||||
return ClassUtils.forName((String) attribute, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Records condition evaluation details for reporting and logging.
|
||||
*
|
||||
* @author Greg Turnquist
|
||||
* @author Dave Syer
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public final class ConditionEvaluationReport {
|
||||
|
||||
private static final String BEAN_NAME = "autoConfigurationReport";
|
||||
|
||||
private static final AncestorsMatchedCondition ANCESTOR_CONDITION = new AncestorsMatchedCondition();
|
||||
|
||||
private final SortedMap<String, ConditionAndOutcomes> outcomes = new TreeMap<>();
|
||||
|
||||
private boolean addedAncestorOutcomes;
|
||||
|
||||
private ConditionEvaluationReport parent;
|
||||
|
||||
private List<String> exclusions = Collections.emptyList();
|
||||
|
||||
private Set<String> unconditionalClasses = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
* @see #get(ConfigurableListableBeanFactory)
|
||||
*/
|
||||
private ConditionEvaluationReport() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the occurrence of condition evaluation.
|
||||
* @param source the source of the condition (class or method name)
|
||||
* @param condition the condition evaluated
|
||||
* @param outcome the condition outcome
|
||||
*/
|
||||
public void recordConditionEvaluation(String source, Condition condition,
|
||||
ConditionOutcome outcome) {
|
||||
Assert.notNull(source, "Source must not be null");
|
||||
Assert.notNull(condition, "Condition must not be null");
|
||||
Assert.notNull(outcome, "Outcome must not be null");
|
||||
this.unconditionalClasses.remove(source);
|
||||
if (!this.outcomes.containsKey(source)) {
|
||||
this.outcomes.put(source, new ConditionAndOutcomes());
|
||||
}
|
||||
this.outcomes.get(source).add(condition, outcome);
|
||||
this.addedAncestorOutcomes = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the names of the classes that have been excluded from condition evaluation.
|
||||
* @param exclusions the names of the excluded classes
|
||||
*/
|
||||
public void recordExclusions(Collection<String> exclusions) {
|
||||
Assert.notNull(exclusions, "exclusions must not be null");
|
||||
this.exclusions = new ArrayList<>(exclusions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the names of the classes that are candidates for condition evaluation.
|
||||
* @param evaluationCandidates the names of the classes whose conditions will be
|
||||
* evaluated
|
||||
*/
|
||||
public void recordEvaluationCandidates(List<String> evaluationCandidates) {
|
||||
Assert.notNull(evaluationCandidates, "evaluationCandidates must not be null");
|
||||
this.unconditionalClasses = new HashSet<>(evaluationCandidates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns condition outcomes from this report, grouped by the source.
|
||||
* @return the condition outcomes
|
||||
*/
|
||||
public Map<String, ConditionAndOutcomes> getConditionAndOutcomesBySource() {
|
||||
if (!this.addedAncestorOutcomes) {
|
||||
for (Map.Entry<String, ConditionAndOutcomes> entry : this.outcomes
|
||||
.entrySet()) {
|
||||
if (!entry.getValue().isFullMatch()) {
|
||||
addNoMatchOutcomeToAncestors(entry.getKey());
|
||||
}
|
||||
}
|
||||
this.addedAncestorOutcomes = true;
|
||||
}
|
||||
return Collections.unmodifiableMap(this.outcomes);
|
||||
}
|
||||
|
||||
private void addNoMatchOutcomeToAncestors(String source) {
|
||||
String prefix = source + "$";
|
||||
for (Entry<String, ConditionAndOutcomes> entry : this.outcomes.entrySet()) {
|
||||
if (entry.getKey().startsWith(prefix)) {
|
||||
ConditionOutcome outcome = ConditionOutcome.noMatch(ConditionMessage
|
||||
.forCondition("Ancestor " + source).because("did not match"));
|
||||
entry.getValue().add(ANCESTOR_CONDITION, outcome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the classes that have been excluded from condition evaluation.
|
||||
* @return the names of the excluded classes
|
||||
*/
|
||||
public List<String> getExclusions() {
|
||||
return Collections.unmodifiableList(this.exclusions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of the classes that were evaluated but were not conditional.
|
||||
* @return the names of the unconditional classes
|
||||
*/
|
||||
public Set<String> getUnconditionalClasses() {
|
||||
return Collections.unmodifiableSet(this.unconditionalClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* The parent report (from a parent BeanFactory if there is one).
|
||||
* @return the parent report (or null if there isn't one)
|
||||
*/
|
||||
public ConditionEvaluationReport getParent() {
|
||||
return this.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a {@link ConditionEvaluationReport} for the specified bean factory.
|
||||
* @param beanFactory the bean factory
|
||||
* @return an existing or new {@link ConditionEvaluationReport}
|
||||
*/
|
||||
public static ConditionEvaluationReport get(
|
||||
ConfigurableListableBeanFactory beanFactory) {
|
||||
synchronized (beanFactory) {
|
||||
ConditionEvaluationReport report;
|
||||
if (beanFactory.containsSingleton(BEAN_NAME)) {
|
||||
report = beanFactory.getBean(BEAN_NAME, ConditionEvaluationReport.class);
|
||||
}
|
||||
else {
|
||||
report = new ConditionEvaluationReport();
|
||||
beanFactory.registerSingleton(BEAN_NAME, report);
|
||||
}
|
||||
locateParent(beanFactory.getParentBeanFactory(), report);
|
||||
return report;
|
||||
}
|
||||
}
|
||||
|
||||
private static void locateParent(BeanFactory beanFactory,
|
||||
ConditionEvaluationReport report) {
|
||||
if (beanFactory != null && report.parent == null
|
||||
&& beanFactory.containsBean(BEAN_NAME)) {
|
||||
report.parent = beanFactory.getBean(BEAN_NAME,
|
||||
ConditionEvaluationReport.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to a number of {@link ConditionAndOutcome} items.
|
||||
*/
|
||||
public static class ConditionAndOutcomes implements Iterable<ConditionAndOutcome> {
|
||||
|
||||
private final Set<ConditionAndOutcome> outcomes = new LinkedHashSet<>();
|
||||
|
||||
public void add(Condition condition, ConditionOutcome outcome) {
|
||||
this.outcomes.add(new ConditionAndOutcome(condition, outcome));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if all outcomes match.
|
||||
* @return {@code true} if a full match
|
||||
*/
|
||||
public boolean isFullMatch() {
|
||||
for (ConditionAndOutcome conditionAndOutcomes : this) {
|
||||
if (!conditionAndOutcomes.getOutcome().isMatch()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ConditionAndOutcome> iterator() {
|
||||
return Collections.unmodifiableSet(this.outcomes).iterator();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides access to a single {@link Condition} and {@link ConditionOutcome}.
|
||||
*/
|
||||
public static class ConditionAndOutcome {
|
||||
|
||||
private final Condition condition;
|
||||
|
||||
private final ConditionOutcome outcome;
|
||||
|
||||
public ConditionAndOutcome(Condition condition, ConditionOutcome outcome) {
|
||||
this.condition = condition;
|
||||
this.outcome = outcome;
|
||||
}
|
||||
|
||||
public Condition getCondition() {
|
||||
return this.condition;
|
||||
}
|
||||
|
||||
public ConditionOutcome getOutcome() {
|
||||
return this.outcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
ConditionAndOutcome other = (ConditionAndOutcome) obj;
|
||||
return (ObjectUtils.nullSafeEquals(this.condition.getClass(),
|
||||
other.condition.getClass())
|
||||
&& ObjectUtils.nullSafeEquals(this.outcome, other.outcome));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.condition.getClass().hashCode() * 31 + this.outcome.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.condition.getClass() + " " + this.outcome;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class AncestorsMatchedCondition implements Condition {
|
||||
|
||||
@Override
|
||||
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationImportEvent;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurationImportListener;
|
||||
|
||||
/**
|
||||
* {@link AutoConfigurationImportListener} to record results with the
|
||||
* {@link ConditionEvaluationReport}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConditionEvaluationReportAutoConfigurationImportListener
|
||||
implements AutoConfigurationImportListener, BeanFactoryAware {
|
||||
|
||||
private ConfigurableListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void onAutoConfigurationImportEvent(AutoConfigurationImportEvent event) {
|
||||
if (this.beanFactory != null) {
|
||||
ConditionEvaluationReport report = ConditionEvaluationReport
|
||||
.get(this.beanFactory);
|
||||
report.recordEvaluationCandidates(event.getCandidateConfigurations());
|
||||
report.recordExclusions(event.getExclusions());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.beanFactory = (beanFactory instanceof ConfigurableListableBeanFactory
|
||||
? (ConfigurableListableBeanFactory) beanFactory : null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A message associated with a {@link ConditionOutcome}. Provides a fluent builder style
|
||||
* API to encourage consistency across all condition messages.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.4.1
|
||||
*/
|
||||
public final class ConditionMessage {
|
||||
|
||||
private String message;
|
||||
|
||||
private ConditionMessage() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
private ConditionMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
private ConditionMessage(ConditionMessage prior, String message) {
|
||||
this.message = (prior.isEmpty() ? message : prior + "; " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return {@code true} if the message is empty.
|
||||
* @return if the message is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return !StringUtils.hasLength(this.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (this.message == null ? "" : this.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHashCode(this.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null || !ConditionMessage.class.isInstance(obj)) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
return ObjectUtils.nullSafeEquals(((ConditionMessage) obj).message, this.message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link ConditionMessage} based on the instance and an appended
|
||||
* message.
|
||||
* @param message the message to append
|
||||
* @return a new {@link ConditionMessage} instance
|
||||
*/
|
||||
public ConditionMessage append(String message) {
|
||||
if (!StringUtils.hasLength(message)) {
|
||||
return this;
|
||||
}
|
||||
if (!StringUtils.hasLength(this.message)) {
|
||||
return new ConditionMessage(message);
|
||||
}
|
||||
|
||||
return new ConditionMessage(this.message + " " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new builder to construct a new {@link ConditionMessage} based on the
|
||||
* instance and a new condition outcome.
|
||||
* @param condition the condition
|
||||
* @param details details of the condition
|
||||
* @return a {@link Builder} builder
|
||||
* @see #andCondition(String, Object...)
|
||||
* @see #forCondition(Class, Object...)
|
||||
*/
|
||||
public Builder andCondition(Class<? extends Annotation> condition,
|
||||
Object... details) {
|
||||
Assert.notNull(condition, "Condition must not be null");
|
||||
return andCondition("@" + ClassUtils.getShortName(condition), details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new builder to construct a new {@link ConditionMessage} based on the
|
||||
* instance and a new condition outcome.
|
||||
* @param condition the condition
|
||||
* @param details details of the condition
|
||||
* @return a {@link Builder} builder
|
||||
* @see #andCondition(Class, Object...)
|
||||
* @see #forCondition(String, Object...)
|
||||
*/
|
||||
public Builder andCondition(String condition, Object... details) {
|
||||
Assert.notNull(condition, "Condition must not be null");
|
||||
String detail = StringUtils.arrayToDelimitedString(details, " ");
|
||||
if (StringUtils.hasLength(detail)) {
|
||||
return new Builder(condition + " " + detail);
|
||||
}
|
||||
return new Builder(condition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to return a new empty {@link ConditionMessage}.
|
||||
* @return a new empty {@link ConditionMessage}
|
||||
*/
|
||||
public static ConditionMessage empty() {
|
||||
return new ConditionMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link ConditionMessage} with a specific message.
|
||||
* @param message the source message (may be a format string if {@code args} are
|
||||
* specified)
|
||||
* @param args format arguments for the message
|
||||
* @return a new {@link ConditionMessage} instance
|
||||
*/
|
||||
public static ConditionMessage of(String message, Object... args) {
|
||||
if (ObjectUtils.isEmpty(args)) {
|
||||
return new ConditionMessage(message);
|
||||
}
|
||||
return new ConditionMessage(String.format(message, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link ConditionMessage} comprised of the specified
|
||||
* messages.
|
||||
* @param messages the source messages (may be {@code null})
|
||||
* @return a new {@link ConditionMessage} instance
|
||||
*/
|
||||
public static ConditionMessage of(Collection<? extends ConditionMessage> messages) {
|
||||
ConditionMessage result = new ConditionMessage();
|
||||
if (messages != null) {
|
||||
for (ConditionMessage message : messages) {
|
||||
result = new ConditionMessage(result, message.toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a builder to construct a new {@link ConditionMessage} for a
|
||||
* condition.
|
||||
* @param condition the condition
|
||||
* @param details details of the condition
|
||||
* @return a {@link Builder} builder
|
||||
* @see #forCondition(String, Object...)
|
||||
* @see #andCondition(String, Object...)
|
||||
*/
|
||||
public static Builder forCondition(Class<? extends Annotation> condition,
|
||||
Object... details) {
|
||||
return new ConditionMessage().andCondition(condition, details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for a builder to construct a new {@link ConditionMessage} for a
|
||||
* condition.
|
||||
* @param condition the condition
|
||||
* @param details details of the condition
|
||||
* @return a {@link Builder} builder
|
||||
* @see #forCondition(Class, Object...)
|
||||
* @see #andCondition(String, Object...)
|
||||
*/
|
||||
public static Builder forCondition(String condition, Object... details) {
|
||||
return new ConditionMessage().andCondition(condition, details);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to create a {@link ConditionMessage} for a condition.
|
||||
*/
|
||||
public final class Builder {
|
||||
|
||||
private final String condition;
|
||||
|
||||
private Builder(String condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that an exact result was found. For example
|
||||
* {@code foundExactly("foo")} results in the message "found foo".
|
||||
* @param result the result that was found
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage foundExactly(Object result) {
|
||||
return found("").items(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that one or more results were found. For example
|
||||
* {@code found("bean").items("x")} results in the message "found bean x".
|
||||
* @param article the article found
|
||||
* @return an {@link ItemsBuilder}
|
||||
*/
|
||||
public ItemsBuilder found(String article) {
|
||||
return found(article, article);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that one or more results were found. For example
|
||||
* {@code found("bean", "beans").items("x", "y")} results in the message "found
|
||||
* beans x, y".
|
||||
* @param singular the article found in singular form
|
||||
* @param plural the article found in plural form
|
||||
* @return an {@link ItemsBuilder}
|
||||
*/
|
||||
public ItemsBuilder found(String singular, String plural) {
|
||||
return new ItemsBuilder(this, "found", singular, plural);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that one or more results were not found. For example
|
||||
* {@code didNotFind("bean").items("x")} results in the message "did not find bean
|
||||
* x".
|
||||
* @param article the article found
|
||||
* @return an {@link ItemsBuilder}
|
||||
*/
|
||||
public ItemsBuilder didNotFind(String article) {
|
||||
return didNotFind(article, article);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that one or more results were found. For example
|
||||
* {@code didNotFind("bean", "beans").items("x", "y")} results in the message "did
|
||||
* not find beans x, y".
|
||||
* @param singular the article found in singular form
|
||||
* @param plural the article found in plural form
|
||||
* @return an {@link ItemsBuilder}
|
||||
*/
|
||||
public ItemsBuilder didNotFind(String singular, String plural) {
|
||||
return new ItemsBuilder(this, "did not find", singular, plural);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a single result. For example {@code resultedIn("yes")} results in the
|
||||
* message "resulted in yes".
|
||||
* @param result the result
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage resultedIn(Object result) {
|
||||
return because("resulted in " + result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates something is available. For example {@code available("money")}
|
||||
* results in the message "money is available".
|
||||
* @param item the item that is available
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage available(String item) {
|
||||
return because(item + " is available");
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates something is not available. For example {@code notAvailable("time")}
|
||||
* results in the message "time is not available".
|
||||
* @param item the item that is not available
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage notAvailable(String item) {
|
||||
return because(item + " is not available");
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the reason. For example {@code reason("running Linux")} results in
|
||||
* the message "running Linux".
|
||||
* @param reason the reason for the message
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage because(String reason) {
|
||||
if (StringUtils.isEmpty(reason)) {
|
||||
return new ConditionMessage(ConditionMessage.this, this.condition);
|
||||
}
|
||||
return new ConditionMessage(ConditionMessage.this, this.condition
|
||||
+ (StringUtils.isEmpty(this.condition) ? "" : " ") + reason);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder used to create a {@link ItemsBuilder} for a condition.
|
||||
*/
|
||||
public final class ItemsBuilder {
|
||||
|
||||
private final Builder condition;
|
||||
|
||||
private final String reason;
|
||||
|
||||
private final String singular;
|
||||
|
||||
private final String plural;
|
||||
|
||||
private ItemsBuilder(Builder condition, String reason, String singular,
|
||||
String plural) {
|
||||
this.condition = condition;
|
||||
this.reason = reason;
|
||||
this.singular = singular;
|
||||
this.plural = plural;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when no items are available. For example
|
||||
* {@code didNotFind("any beans").atAll()} results in the message "did not find
|
||||
* any beans".
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage atAll() {
|
||||
return items(Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the items. For example
|
||||
* {@code didNotFind("bean", "beans").items("x", "y")} results in the message "did
|
||||
* not find beans x, y".
|
||||
* @param items the items (may be {@code null})
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage items(Object... items) {
|
||||
return items(Style.NORMAL, items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the items. For example
|
||||
* {@code didNotFind("bean", "beans").items("x", "y")} results in the message "did
|
||||
* not find beans x, y".
|
||||
* @param style the render style
|
||||
* @param items the items (may be {@code null})
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage items(Style style, Object... items) {
|
||||
return items(style,
|
||||
items == null ? (Collection<?>) null : Arrays.asList(items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the items. For example
|
||||
* {@code didNotFind("bean", "beans").items(Collections.singleton("x")} results in
|
||||
* the message "did not find bean x".
|
||||
* @param items the source of the items (may be {@code null})
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage items(Collection<?> items) {
|
||||
return items(Style.NORMAL, items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the items with a {@link Style}. For example
|
||||
* {@code didNotFind("bean", "beans").items(Style.QUOTE, Collections.singleton("x")}
|
||||
* results in the message "did not find bean 'x'".
|
||||
* @param style the render style
|
||||
* @param items the source of the items (may be {@code null})
|
||||
* @return a built {@link ConditionMessage}
|
||||
*/
|
||||
public ConditionMessage items(Style style, Collection<?> items) {
|
||||
Assert.notNull(style, "Style must not be null");
|
||||
StringBuilder message = new StringBuilder(this.reason);
|
||||
items = style.applyTo(items);
|
||||
if ((this.condition == null || items.size() <= 1)
|
||||
&& StringUtils.hasLength(this.singular)) {
|
||||
message.append(" " + this.singular);
|
||||
}
|
||||
else if (StringUtils.hasLength(this.plural)) {
|
||||
message.append(" " + this.plural);
|
||||
}
|
||||
if (items != null && !items.isEmpty()) {
|
||||
message.append(
|
||||
" " + StringUtils.collectionToDelimitedString(items, ", "));
|
||||
}
|
||||
return this.condition.because(message.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Render styles.
|
||||
*/
|
||||
public enum Style {
|
||||
|
||||
NORMAL {
|
||||
@Override
|
||||
protected Object applyToItem(Object item) {
|
||||
return item;
|
||||
}
|
||||
},
|
||||
|
||||
QUOTE {
|
||||
@Override
|
||||
protected String applyToItem(Object item) {
|
||||
return (item == null ? null : "'" + item + "'");
|
||||
}
|
||||
};
|
||||
|
||||
public Collection<?> applyTo(Collection<?> items) {
|
||||
List<Object> result = new ArrayList<>();
|
||||
for (Object item : items) {
|
||||
result.add(applyToItem(item));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected abstract Object applyToItem(Object item);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Outcome for a condition match, including log message.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ConditionMessage
|
||||
*/
|
||||
public class ConditionOutcome {
|
||||
|
||||
private final boolean match;
|
||||
|
||||
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) {
|
||||
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.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ConditionOutcome} instance for a 'match'.
|
||||
* @return the {@link ConditionOutcome}
|
||||
*/
|
||||
public static ConditionOutcome match() {
|
||||
return match(ConditionMessage.empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link ConditionOutcome} instance for 'match'. For more consistent
|
||||
* messages consider using {@link #match(ConditionMessage)}.
|
||||
* @param message the message
|
||||
* @return the {@link ConditionOutcome}
|
||||
*/
|
||||
public static ConditionOutcome match(String message) {
|
||||
return new ConditionOutcome(true, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return the {@link ConditionOutcome}
|
||||
*/
|
||||
public static ConditionOutcome noMatch(String 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 matches
|
||||
*/
|
||||
public boolean isMatch() {
|
||||
return this.match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an outcome message or {@code null}.
|
||||
* @return the message or {@code null}
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Boolean.hashCode(this.match) * 31
|
||||
+ ObjectUtils.nullSafeHashCode(this.message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() == obj.getClass()) {
|
||||
ConditionOutcome other = (ConditionOutcome) obj;
|
||||
return (this.match == other.match
|
||||
&& ObjectUtils.nullSafeEquals(this.message, other.message));
|
||||
}
|
||||
return super.equals(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return (this.message == null ? "" : this.message.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inverse of the specified condition outcome.
|
||||
* @param outcome the outcome to inverse
|
||||
* @return the inverse of the condition outcome
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public static ConditionOutcome inverse(ConditionOutcome outcome) {
|
||||
return new ConditionOutcome(!outcome.isMatch(), outcome.getConditionMessage());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified bean classes and/or names are
|
||||
* already contained in the {@link BeanFactory}. When placed on a {@code @Bean} method,
|
||||
* the bean class defaults to the return type of the factory method:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class MyAutoConfiguration {
|
||||
*
|
||||
* @ConditionalOnBean
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* In the sample above the condition will match if a bean of type {@code MyService} is
|
||||
* already contained in the {@link BeanFactory}.
|
||||
* <p>
|
||||
* The condition can only match the bean definitions that have been processed by the
|
||||
* application context so far and, as such, it is strongly recommended to use this
|
||||
* condition on auto-configuration classes only. If a candidate bean may be created by
|
||||
* another auto-configuration, make sure that the one using this condition runs after.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnBeanCondition.class)
|
||||
public @interface ConditionalOnBean {
|
||||
|
||||
/**
|
||||
* The class type of bean that should be checked. The condition matches when all of
|
||||
* the classes specified are contained in the {@link ApplicationContext}.
|
||||
* @return the class types of beans to check
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The class type names of bean that should be checked. The condition matches when all
|
||||
* of the classes specified are contained in the {@link ApplicationContext}.
|
||||
* @return the class type names of beans to check
|
||||
*/
|
||||
String[] type() default {};
|
||||
|
||||
/**
|
||||
* The annotation type decorating a bean that should be checked. The condition matches
|
||||
* when all of the annotations specified are defined on beans in the
|
||||
* {@link ApplicationContext}.
|
||||
* @return the class-level annotation types to check
|
||||
*/
|
||||
Class<? extends Annotation>[] annotation() default {};
|
||||
|
||||
/**
|
||||
* The names of beans to check. The condition matches when all of the bean names
|
||||
* specified are contained in the {@link ApplicationContext}.
|
||||
* @return the name of beans to check
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* Strategy to decide if the application context hierarchy (parent contexts) should be
|
||||
* considered.
|
||||
* @return the search strategy
|
||||
*/
|
||||
SearchStrategy search() default SearchStrategy.ALL;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified classes are on the classpath.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnClassCondition.class)
|
||||
public @interface ConditionalOnClass {
|
||||
|
||||
/**
|
||||
* The classes that must be present. Since this annotation is parsed by loading class
|
||||
* bytecode, it is safe to specify classes here that may ultimately not be on the
|
||||
* classpath, only if this annotation is directly on the affected component and
|
||||
* <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
|
||||
* use this annotation as a meta-annotation, only use the {@link #name} attribute.
|
||||
* @return the classes that must be present
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The classes names that must be present.
|
||||
* @return the class names that must be present.
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that matches when the specified cloud platform is active.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @since 1.5.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnCloudPlatformCondition.class)
|
||||
public @interface ConditionalOnCloudPlatform {
|
||||
|
||||
/**
|
||||
* The {@link CloudPlatform cloud platform} that must be active.
|
||||
* @return the expected cloud platform
|
||||
*/
|
||||
CloudPlatform value();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* Configuration annotation for a conditional element that depends on the value of a SpEL
|
||||
* expression.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Documented
|
||||
@Conditional(OnExpressionCondition.class)
|
||||
public @interface ConditionalOnExpression {
|
||||
|
||||
/**
|
||||
* The SpEL expression to evaluate. Expression should return {@code true} if the
|
||||
* condition passes or {@code false} if it fails.
|
||||
* @return the SpEL expression
|
||||
*/
|
||||
String value() default "true";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.system.JavaVersion;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that matches based on the JVM version the application is running
|
||||
* on.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnJavaCondition.class)
|
||||
public @interface ConditionalOnJava {
|
||||
|
||||
/**
|
||||
* Configures whether the value configured in {@link #value()} shall be considered the
|
||||
* upper exclusive or lower inclusive boundary. Defaults to
|
||||
* {@link Range#EQUAL_OR_NEWER}.
|
||||
* @return the range
|
||||
*/
|
||||
Range range() default Range.EQUAL_OR_NEWER;
|
||||
|
||||
/**
|
||||
* The {@link JavaVersion} to check for. Use {@link #range()} to specify whether the
|
||||
* configured value is an upper-exclusive or lower-inclusive boundary.
|
||||
* @return the java version
|
||||
*/
|
||||
JavaVersion value();
|
||||
|
||||
/**
|
||||
* Range options.
|
||||
*/
|
||||
enum Range {
|
||||
|
||||
/**
|
||||
* Equal to, or newer than the specified {@link JavaVersion}.
|
||||
*/
|
||||
EQUAL_OR_NEWER,
|
||||
|
||||
/**
|
||||
* Older than the specified {@link JavaVersion}.
|
||||
*/
|
||||
OLDER_THAN
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that matches based on the availability of a JNDI
|
||||
* {@link InitialContext} and the ability to lookup specific locations.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnJndiCondition.class)
|
||||
public @interface ConditionalOnJndi {
|
||||
|
||||
/**
|
||||
* JNDI Locations, one of which must exist. If no locations are specific the condition
|
||||
* matches solely based on the presence of an {@link InitialContext}.
|
||||
* @return the JNDI locations
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified bean classes and/or names are
|
||||
* not already contained in the {@link BeanFactory}.
|
||||
* <p>
|
||||
* When placed on a {@code @Bean} method, the bean class defaults to the return type of
|
||||
* the factory method:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* public class MyAutoConfiguration {
|
||||
*
|
||||
* @ConditionalOnMissingBean
|
||||
* @Bean
|
||||
* public MyService myService() {
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* In the sample above the condition will match if no bean of type {@code MyService} is
|
||||
* already contained in the {@link BeanFactory}.
|
||||
* <p>
|
||||
* The condition can only match the bean definitions that have been processed by the
|
||||
* application context so far and, as such, it is strongly recommended to use this
|
||||
* condition on auto-configuration classes only. If a candidate bean may be created by
|
||||
* another auto-configuration, make sure that the one using this condition runs after.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnBeanCondition.class)
|
||||
public @interface ConditionalOnMissingBean {
|
||||
|
||||
/**
|
||||
* The class type of bean that should be checked. The condition matches when each
|
||||
* class specified is missing in the {@link ApplicationContext}.
|
||||
* @return the class types of beans to check
|
||||
*/
|
||||
Class<?>[] value() default {};
|
||||
|
||||
/**
|
||||
* The class type names of bean that should be checked. The condition matches when
|
||||
* each class specified is missing in the {@link ApplicationContext}.
|
||||
* @return the class type names of beans to check
|
||||
*/
|
||||
String[] type() default {};
|
||||
|
||||
/**
|
||||
* The class type of beans that should be ignored when identifying matching beans.
|
||||
* @return the class types of beans to ignore
|
||||
* @since 1.2.5
|
||||
*/
|
||||
Class<?>[] ignored() default {};
|
||||
|
||||
/**
|
||||
* The class type names of beans that should be ignored when identifying matching
|
||||
* beans.
|
||||
* @return the class type names of beans to ignore
|
||||
* @since 1.2.5
|
||||
*/
|
||||
String[] ignoredType() default {};
|
||||
|
||||
/**
|
||||
* The annotation type decorating a bean that should be checked. The condition matches
|
||||
* when each annotation specified is missing from all beans in the
|
||||
* {@link ApplicationContext}.
|
||||
* @return the class-level annotation types to check
|
||||
*/
|
||||
Class<? extends Annotation>[] annotation() default {};
|
||||
|
||||
/**
|
||||
* The names of beans to check. The condition matches when each bean name specified is
|
||||
* missing in the {@link ApplicationContext}.
|
||||
* @return the name of beans to check
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* Strategy to decide if the application context hierarchy (parent contexts) should be
|
||||
* considered.
|
||||
* @return the search strategy
|
||||
*/
|
||||
SearchStrategy search() default SearchStrategy.ALL;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified classes are not on the
|
||||
* classpath.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnClassCondition.class)
|
||||
public @interface ConditionalOnMissingClass {
|
||||
|
||||
/**
|
||||
* The names of the classes that must not be present.
|
||||
* @return the names of the classes that must not be present
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the application context is a not a web
|
||||
* application context.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnWebApplicationCondition.class)
|
||||
public @interface ConditionalOnNotWebApplication {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that checks if the specified properties have a specific value. By
|
||||
* default the properties must be present in the {@link Environment} and
|
||||
* <strong>not</strong> equal to {@code false}. The {@link #havingValue()} and
|
||||
* {@link #matchIfMissing()} attributes allow further customizations.
|
||||
*
|
||||
* <p>
|
||||
* The {@link #havingValue} attribute can be used to specify the value that the property
|
||||
* should have. The table below shows when a condition matches according to the property
|
||||
* value and the {@link #havingValue()} attribute:
|
||||
*
|
||||
* <table summary="having values" border="1">
|
||||
* <tr>
|
||||
* <th>Property Value</th>
|
||||
* <th>{@code havingValue=""}</th>
|
||||
* <th>{@code havingValue="true"}</th>
|
||||
* <th>{@code havingValue="false"}</th>
|
||||
* <th>{@code havingValue="foo"}</th>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code "true"}</td>
|
||||
* <td>yes</td>
|
||||
* <td>yes</td>
|
||||
* <td>no</td>
|
||||
* <td>no</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code "false"}</td>
|
||||
* <td>no</td>
|
||||
* <td>no</td>
|
||||
* <td>yes</td>
|
||||
* <td>no</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>{@code "foo"}</td>
|
||||
* <td>yes</td>
|
||||
* <td>no</td>
|
||||
* <td>no</td>
|
||||
* <td>yes</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*
|
||||
* <p>
|
||||
* If the property is not contained in the {@link Environment} at all, the
|
||||
* {@link #matchIfMissing()} attribute is consulted. By default missing attributes do not
|
||||
* match.
|
||||
*
|
||||
* @author Maciej Walkowiak
|
||||
* @author Stephane Nicoll
|
||||
* @author Phillip Webb
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Documented
|
||||
@Conditional(OnPropertyCondition.class)
|
||||
public @interface ConditionalOnProperty {
|
||||
|
||||
/**
|
||||
* Alias for {@link #name()}.
|
||||
* @return the names
|
||||
*/
|
||||
String[] value() default {};
|
||||
|
||||
/**
|
||||
* A prefix that should be applied to each property. The prefix automatically ends
|
||||
* with a dot if not specified.
|
||||
* @return the prefix
|
||||
*/
|
||||
String prefix() default "";
|
||||
|
||||
/**
|
||||
* The name of the properties to test. If a prefix has been defined, it is applied to
|
||||
* compute the full key of each property. For instance if the prefix is
|
||||
* {@code app.config} and one value is {@code my-value}, the fully key would be
|
||||
* {@code app.config.my-value}
|
||||
* <p>
|
||||
* Use the dashed notation to specify each property, that is all lower case with a "-"
|
||||
* to separate words (e.g. {@code my-long-property}).
|
||||
* @return the names
|
||||
*/
|
||||
String[] name() default {};
|
||||
|
||||
/**
|
||||
* The string representation of the expected value for the properties. If not
|
||||
* specified, the property must <strong>not</strong> be equals to {@code false}.
|
||||
* @return the expected value
|
||||
*/
|
||||
String havingValue() default "";
|
||||
|
||||
/**
|
||||
* Specify if the condition should match if the property is not set. Defaults to
|
||||
* {@code false}.
|
||||
* @return if should match if the property is missing
|
||||
*/
|
||||
boolean matchIfMissing() default false;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified resources are on the
|
||||
* classpath.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnResourceCondition.class)
|
||||
public @interface ConditionalOnResource {
|
||||
|
||||
/**
|
||||
* The resources that must be present.
|
||||
* @return the resource paths that must be present.
|
||||
*/
|
||||
String[] resources() default {};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that only matches when the specified bean class is already
|
||||
* contained in the {@link BeanFactory} and a single candidate can be determined.
|
||||
* <p>
|
||||
* The condition will also match if multiple matching bean instances are already contained
|
||||
* in the {@link BeanFactory} but a primary candidate has been defined; essentially, the
|
||||
* condition match if auto-wiring a bean with the defined type will succeed.
|
||||
* <p>
|
||||
* The condition can only match the bean definitions that have been processed by the
|
||||
* application context so far and, as such, it is strongly recommended to use this
|
||||
* condition on auto-configuration classes only. If a candidate bean may be created by
|
||||
* another auto-configuration, make sure that the one using this condition runs after.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 1.3.0
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnBeanCondition.class)
|
||||
public @interface ConditionalOnSingleCandidate {
|
||||
|
||||
/**
|
||||
* The class type of bean that should be checked. The condition match if the class
|
||||
* specified is contained in the {@link ApplicationContext} and a primary candidate
|
||||
* exists in case of multiple instances.
|
||||
* <p>
|
||||
* This attribute may <strong>not</strong> be used in conjunction with {@link #type()}
|
||||
* , but it may be used instead of {@link #type()}.
|
||||
* @return the class type of the bean to check
|
||||
*/
|
||||
Class<?> value() default Object.class;
|
||||
|
||||
/**
|
||||
* The class type name of bean that should be checked. The condition matches if the
|
||||
* class specified is contained in the {@link ApplicationContext} and a primary
|
||||
* candidate exists in case of multiple instances.
|
||||
* <p>
|
||||
* This attribute may <strong>not</strong> be used in conjunction with
|
||||
* {@link #value()}, but it may be used instead of {@link #value()}.
|
||||
* @return the class type name of the bean to check
|
||||
*/
|
||||
String type() default "";
|
||||
|
||||
/**
|
||||
* Strategy to decide if the application context hierarchy (parent contexts) should be
|
||||
* considered.
|
||||
* @return the search strategy
|
||||
*/
|
||||
SearchStrategy search() default SearchStrategy.ALL;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
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;
|
||||
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
|
||||
/**
|
||||
* {@link Conditional} that matches when the application is a web application. By default,
|
||||
* any web application will match but it can be narrowed using the {@link #type()}
|
||||
* attribute.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Conditional(OnWebApplicationCondition.class)
|
||||
public @interface ConditionalOnWebApplication {
|
||||
|
||||
/**
|
||||
* The required type of the web application.
|
||||
* @return the required web application type
|
||||
*/
|
||||
Type type() default Type.ANY;
|
||||
|
||||
/**
|
||||
* Available application types.
|
||||
*/
|
||||
enum Type {
|
||||
|
||||
/**
|
||||
* Any web application will match.
|
||||
*/
|
||||
ANY,
|
||||
|
||||
/**
|
||||
* Only servlet-based web application will match.
|
||||
*/
|
||||
SERVLET,
|
||||
|
||||
/**
|
||||
* Only reactive-based web application will match.
|
||||
*/
|
||||
REACTIVE
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
|
||||
/**
|
||||
* {@link Condition} that will match when none of the nested class conditions match. Can
|
||||
* be used to create composite conditions, for example:
|
||||
*
|
||||
* <pre class="code">
|
||||
* static class OnNeitherJndiNorProperty extends NoneOfNestedConditions {
|
||||
*
|
||||
* OnNeitherJndiNorProperty() {
|
||||
* super(ConfigurationPhase.PARSE_CONFIGURATION);
|
||||
* }
|
||||
*
|
||||
* @ConditionalOnJndi()
|
||||
* static class OnJndi {
|
||||
* }
|
||||
*
|
||||
* @ConditionalOnProperty("something")
|
||||
* static class OnProperty {
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* </pre>
|
||||
* <p>
|
||||
* The
|
||||
* {@link org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase
|
||||
* ConfigurationPhase} should be specified according to the conditions that are defined.
|
||||
* In the example above, all conditions are static and can be evaluated early so
|
||||
* {@code PARSE_CONFIGURATION} is a right fit.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.3.0
|
||||
*/
|
||||
public abstract class NoneNestedConditions extends AbstractNestedCondition {
|
||||
|
||||
public NoneNestedConditions(ConfigurationPhase configurationPhase) {
|
||||
super(configurationPhase);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
|
||||
boolean match = memberOutcomes.getMatches().isEmpty();
|
||||
List<ConditionMessage> messages = new ArrayList<>();
|
||||
messages.add(ConditionMessage.forCondition("NoneNestedConditions")
|
||||
.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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,581 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
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.Style;
|
||||
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.Ordered;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for the presence or absence of specific beans.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Dave Syer
|
||||
* @author Jakub Kubrynski
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE)
|
||||
class OnBeanCondition 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(ConditionalOnBean.class.getName())) {
|
||||
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
|
||||
ConditionalOnBean.class);
|
||||
MatchResult matchResult = getMatchingBeans(context, spec);
|
||||
if (!matchResult.isAllMatched()) {
|
||||
String reason = createOnBeanNoMatchReason(matchResult);
|
||||
return ConditionOutcome.noMatch(ConditionMessage
|
||||
.forCondition(ConditionalOnBean.class, spec).because(reason));
|
||||
}
|
||||
matchMessage = matchMessage.andCondition(ConditionalOnBean.class, spec)
|
||||
.found("bean", "beans")
|
||||
.items(Style.QUOTE, matchResult.getNamesOfAllMatches());
|
||||
}
|
||||
if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
|
||||
BeanSearchSpec spec = new SingleCandidateBeanSearchSpec(context, metadata,
|
||||
ConditionalOnSingleCandidate.class);
|
||||
MatchResult matchResult = getMatchingBeans(context, spec);
|
||||
if (!matchResult.isAllMatched()) {
|
||||
return ConditionOutcome.noMatch(ConditionMessage
|
||||
.forCondition(ConditionalOnSingleCandidate.class, spec)
|
||||
.didNotFind("any beans").atAll());
|
||||
}
|
||||
else if (!hasSingleAutowireCandidate(context.getBeanFactory(),
|
||||
matchResult.getNamesOfAllMatches(),
|
||||
spec.getStrategy() == SearchStrategy.ALL)) {
|
||||
return ConditionOutcome.noMatch(ConditionMessage
|
||||
.forCondition(ConditionalOnSingleCandidate.class, spec)
|
||||
.didNotFind("a primary bean from beans")
|
||||
.items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
|
||||
}
|
||||
matchMessage = matchMessage
|
||||
.andCondition(ConditionalOnSingleCandidate.class, spec)
|
||||
.found("a primary bean from beans")
|
||||
.items(Style.QUOTE, matchResult.namesOfAllMatches);
|
||||
}
|
||||
if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
|
||||
BeanSearchSpec spec = new BeanSearchSpec(context, metadata,
|
||||
ConditionalOnMissingBean.class);
|
||||
MatchResult matchResult = getMatchingBeans(context, spec);
|
||||
if (matchResult.isAnyMatched()) {
|
||||
String reason = createOnMissingBeanNoMatchReason(matchResult);
|
||||
return ConditionOutcome.noMatch(ConditionMessage
|
||||
.forCondition(ConditionalOnMissingBean.class, spec)
|
||||
.because(reason));
|
||||
}
|
||||
matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, spec)
|
||||
.didNotFind("any beans").atAll();
|
||||
}
|
||||
return ConditionOutcome.match(matchMessage);
|
||||
}
|
||||
|
||||
private String createOnBeanNoMatchReason(MatchResult matchResult) {
|
||||
StringBuilder reason = new StringBuilder();
|
||||
appendMessageForNoMatches(reason, matchResult.unmatchedAnnotations,
|
||||
"annotated with");
|
||||
appendMessageForNoMatches(reason, matchResult.unmatchedTypes, "of type");
|
||||
appendMessageForNoMatches(reason, matchResult.unmatchedNames, "named");
|
||||
return reason.toString();
|
||||
}
|
||||
|
||||
private void appendMessageForNoMatches(StringBuilder reason,
|
||||
Collection<String> unmatched, String description) {
|
||||
if (!unmatched.isEmpty()) {
|
||||
if (reason.length() > 0) {
|
||||
reason.append(" and ");
|
||||
}
|
||||
reason.append("did not find any beans ");
|
||||
reason.append(description);
|
||||
reason.append(" ");
|
||||
reason.append(StringUtils.collectionToDelimitedString(unmatched, ", "));
|
||||
}
|
||||
}
|
||||
|
||||
private String createOnMissingBeanNoMatchReason(MatchResult matchResult) {
|
||||
StringBuilder reason = new StringBuilder();
|
||||
appendMessageForMatches(reason, matchResult.matchedAnnotations, "annotated with");
|
||||
appendMessageForMatches(reason, matchResult.matchedTypes, "of type");
|
||||
if (!matchResult.matchedNames.isEmpty()) {
|
||||
if (reason.length() > 0) {
|
||||
reason.append(" and ");
|
||||
}
|
||||
reason.append("found beans named ");
|
||||
reason.append(StringUtils
|
||||
.collectionToDelimitedString(matchResult.matchedNames, ", "));
|
||||
}
|
||||
return reason.toString();
|
||||
}
|
||||
|
||||
private void appendMessageForMatches(StringBuilder reason,
|
||||
Map<String, Collection<String>> matches, String description) {
|
||||
if (!matches.isEmpty()) {
|
||||
for (Map.Entry<String, Collection<String>> match : matches.entrySet()) {
|
||||
if (reason.length() > 0) {
|
||||
reason.append(" and ");
|
||||
}
|
||||
reason.append("found beans ");
|
||||
reason.append(description);
|
||||
reason.append("'");
|
||||
reason.append(match.getKey());
|
||||
reason.append("'");
|
||||
reason.append(
|
||||
StringUtils.collectionToDelimitedString(match.getValue(), ", "));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private MatchResult getMatchingBeans(ConditionContext context, BeanSearchSpec beans) {
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
if (beans.getStrategy() == SearchStrategy.ANCESTORS) {
|
||||
BeanFactory parent = beanFactory.getParentBeanFactory();
|
||||
Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
|
||||
"Unable to use SearchStrategy.PARENTS");
|
||||
beanFactory = (ConfigurableListableBeanFactory) parent;
|
||||
}
|
||||
MatchResult matchResult = new MatchResult();
|
||||
boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
|
||||
List<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(
|
||||
beans.getIgnoredTypes(), beanFactory, context, considerHierarchy);
|
||||
for (String type : beans.getTypes()) {
|
||||
Collection<String> typeMatches = getBeanNamesForType(beanFactory, type,
|
||||
context.getClassLoader(), considerHierarchy);
|
||||
typeMatches.removeAll(beansIgnoredByType);
|
||||
if (typeMatches.isEmpty()) {
|
||||
matchResult.recordUnmatchedType(type);
|
||||
}
|
||||
else {
|
||||
matchResult.recordMatchedType(type, typeMatches);
|
||||
}
|
||||
}
|
||||
for (String annotation : beans.getAnnotations()) {
|
||||
List<String> annotationMatches = Arrays
|
||||
.asList(getBeanNamesForAnnotation(beanFactory, annotation,
|
||||
context.getClassLoader(), considerHierarchy));
|
||||
annotationMatches.removeAll(beansIgnoredByType);
|
||||
if (annotationMatches.isEmpty()) {
|
||||
matchResult.recordUnmatchedAnnotation(annotation);
|
||||
}
|
||||
else {
|
||||
matchResult.recordMatchedAnnotation(annotation, annotationMatches);
|
||||
}
|
||||
}
|
||||
for (String beanName : beans.getNames()) {
|
||||
if (!beansIgnoredByType.contains(beanName)
|
||||
&& containsBean(beanFactory, beanName, considerHierarchy)) {
|
||||
matchResult.recordMatchedName(beanName);
|
||||
}
|
||||
else {
|
||||
matchResult.recordUnmatchedName(beanName);
|
||||
}
|
||||
}
|
||||
return matchResult;
|
||||
}
|
||||
|
||||
private List<String> getNamesOfBeansIgnoredByType(List<String> ignoredTypes,
|
||||
ListableBeanFactory beanFactory, ConditionContext context,
|
||||
boolean considerHierarchy) {
|
||||
List<String> beanNames = new ArrayList<>();
|
||||
for (String ignoredType : ignoredTypes) {
|
||||
beanNames.addAll(getBeanNamesForType(beanFactory, ignoredType,
|
||||
context.getClassLoader(), considerHierarchy));
|
||||
}
|
||||
return beanNames;
|
||||
}
|
||||
|
||||
private boolean containsBean(ConfigurableListableBeanFactory beanFactory,
|
||||
String beanName, boolean considerHierarchy) {
|
||||
if (considerHierarchy) {
|
||||
return beanFactory.containsBean(beanName);
|
||||
}
|
||||
return beanFactory.containsLocalBean(beanName);
|
||||
}
|
||||
|
||||
private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory,
|
||||
String type, ClassLoader classLoader, boolean considerHierarchy)
|
||||
throws LinkageError {
|
||||
try {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
collectBeanNamesForType(result, beanFactory,
|
||||
ClassUtils.forName(type, classLoader), considerHierarchy);
|
||||
return result;
|
||||
}
|
||||
catch (ClassNotFoundException | NoClassDefFoundError ex) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private void collectBeanNamesForType(Set<String> 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 String[] getBeanNamesForAnnotation(
|
||||
ConfigurableListableBeanFactory beanFactory, String type,
|
||||
ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
|
||||
Set<String> 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 StringUtils.toStringArray(names);
|
||||
}
|
||||
|
||||
private void collectBeanNamesForAnnotation(Set<String> 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, Set<String> beanNames,
|
||||
boolean considerHierarchy) {
|
||||
return (beanNames.size() == 1
|
||||
|| getPrimaryBeans(beanFactory, beanNames, considerHierarchy)
|
||||
.size() == 1);
|
||||
}
|
||||
|
||||
private List<String> getPrimaryBeans(ConfigurableListableBeanFactory beanFactory,
|
||||
Set<String> beanNames, boolean considerHierarchy) {
|
||||
List<String> 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<String> names = new ArrayList<>();
|
||||
|
||||
private final List<String> types = new ArrayList<>();
|
||||
|
||||
private final List<String> annotations = new ArrayList<>();
|
||||
|
||||
private final List<String> ignoredTypes = new ArrayList<>();
|
||||
|
||||
private final SearchStrategy strategy;
|
||||
|
||||
BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata,
|
||||
Class<?> annotationType) {
|
||||
this.annotationType = annotationType;
|
||||
MultiValueMap<String, Object> 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<String, Object> attributes, String key,
|
||||
List<String> 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<String> beanTypes) {
|
||||
if (metadata instanceof MethodMetadata
|
||||
&& metadata.isAnnotated(Bean.class.getName())) {
|
||||
addDeducedBeanTypeForBeanMethod(context, (MethodMetadata) metadata,
|
||||
beanTypes);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDeducedBeanTypeForBeanMethod(ConditionContext context,
|
||||
MethodMetadata metadata, final List<String> 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<String> getNames() {
|
||||
return this.names;
|
||||
}
|
||||
|
||||
public List<String> getTypes() {
|
||||
return this.types;
|
||||
}
|
||||
|
||||
public List<String> getAnnotations() {
|
||||
return this.annotations;
|
||||
}
|
||||
|
||||
public List<String> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class SingleCandidateBeanSearchSpec extends BeanSearchSpec {
|
||||
|
||||
SingleCandidateBeanSearchSpec(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata, Class<?> annotationType) {
|
||||
super(context, metadata, annotationType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void collect(MultiValueMap<String, Object> attributes, String key,
|
||||
List<String> destination) {
|
||||
super.collect(attributes, key, destination);
|
||||
destination.removeAll(Arrays.asList("", Object.class.getName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validate(BeanTypeDeductionException ex) {
|
||||
Assert.isTrue(getTypes().size() == 1, annotationName() + " annotations must "
|
||||
+ "specify only one type (got " + getTypes() + ")");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class BeanTypeDeductionException extends RuntimeException {
|
||||
|
||||
private BeanTypeDeductionException(String className, String beanMethodName,
|
||||
Throwable cause) {
|
||||
super("Failed to deduce bean type for " + className + "." + beanMethodName,
|
||||
cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class MatchResult {
|
||||
|
||||
private final Map<String, Collection<String>> matchedAnnotations = new HashMap<>();
|
||||
|
||||
private final List<String> matchedNames = new ArrayList<>();
|
||||
|
||||
private final Map<String, Collection<String>> matchedTypes = new HashMap<>();
|
||||
|
||||
private final List<String> unmatchedAnnotations = new ArrayList<>();
|
||||
|
||||
private final List<String> unmatchedNames = new ArrayList<>();
|
||||
|
||||
private final List<String> unmatchedTypes = new ArrayList<>();
|
||||
|
||||
private final Set<String> namesOfAllMatches = new HashSet<>();
|
||||
|
||||
private void recordMatchedName(String name) {
|
||||
this.matchedNames.add(name);
|
||||
this.namesOfAllMatches.add(name);
|
||||
}
|
||||
|
||||
private void recordUnmatchedName(String name) {
|
||||
this.unmatchedNames.add(name);
|
||||
}
|
||||
|
||||
private void recordMatchedAnnotation(String annotation,
|
||||
Collection<String> matchingNames) {
|
||||
this.matchedAnnotations.put(annotation, matchingNames);
|
||||
this.namesOfAllMatches.addAll(matchingNames);
|
||||
}
|
||||
|
||||
private void recordUnmatchedAnnotation(String annotation) {
|
||||
this.unmatchedAnnotations.add(annotation);
|
||||
}
|
||||
|
||||
private void recordMatchedType(String type, Collection<String> matchingNames) {
|
||||
this.matchedTypes.put(type, matchingNames);
|
||||
this.namesOfAllMatches.addAll(matchingNames);
|
||||
}
|
||||
|
||||
private void recordUnmatchedType(String type) {
|
||||
this.unmatchedTypes.add(type);
|
||||
}
|
||||
|
||||
private boolean isAllMatched() {
|
||||
return this.unmatchedAnnotations.isEmpty() && this.unmatchedNames.isEmpty()
|
||||
&& this.unmatchedTypes.isEmpty();
|
||||
}
|
||||
|
||||
private boolean isAnyMatched() {
|
||||
return (!this.matchedAnnotations.isEmpty()) || (!this.matchedNames.isEmpty())
|
||||
|| (!this.matchedTypes.isEmpty());
|
||||
}
|
||||
|
||||
private Set<String> getNamesOfAllMatches() {
|
||||
return this.namesOfAllMatches;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.security.AccessControlException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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.AutoConfigurationMetadata;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link Condition} and {@link AutoConfigurationImportFilter} that checks for the
|
||||
* presence or absence of specific classes.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnClass
|
||||
* @see ConditionalOnMissingClass
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
class OnClassCondition extends SpringBootCondition
|
||||
implements AutoConfigurationImportFilter, BeanFactoryAware, BeanClassLoaderAware {
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
private ClassLoader beanClassLoader;
|
||||
|
||||
@Override
|
||||
public boolean[] match(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) {
|
||||
// Split the work and perform half in a background thread. Using a single
|
||||
// additional thread seems to offer the best performance. More threads make
|
||||
// things worse
|
||||
int split = autoConfigurationClasses.length / 2;
|
||||
OutcomesResolver firstHalfResolver = createOutcomesResolver(
|
||||
autoConfigurationClasses, 0, split, autoConfigurationMetadata);
|
||||
OutcomesResolver secondHalfResolver = new StandardOutcomesResolver(
|
||||
autoConfigurationClasses, split, autoConfigurationClasses.length,
|
||||
autoConfigurationMetadata, this.beanClassLoader);
|
||||
ConditionOutcome[] secondHalf = secondHalfResolver.resolveOutcomes();
|
||||
ConditionOutcome[] firstHalf = firstHalfResolver.resolveOutcomes();
|
||||
ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];
|
||||
System.arraycopy(firstHalf, 0, outcomes, 0, firstHalf.length);
|
||||
System.arraycopy(secondHalf, 0, outcomes, split, secondHalf.length);
|
||||
return outcomes;
|
||||
}
|
||||
|
||||
private OutcomesResolver createOutcomesResolver(String[] autoConfigurationClasses,
|
||||
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
OutcomesResolver outcomesResolver = new StandardOutcomesResolver(
|
||||
autoConfigurationClasses, start, end, autoConfigurationMetadata,
|
||||
this.beanClassLoader);
|
||||
try {
|
||||
return new ThreadedOutcomesResolver(outcomesResolver);
|
||||
}
|
||||
catch (AccessControlException ex) {
|
||||
return outcomesResolver;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
ClassLoader classLoader = context.getClassLoader();
|
||||
ConditionMessage matchMessage = ConditionMessage.empty();
|
||||
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
|
||||
if (onClasses != null) {
|
||||
List<String> missing = getMatches(onClasses, MatchType.MISSING, classLoader);
|
||||
if (!missing.isEmpty()) {
|
||||
return ConditionOutcome
|
||||
.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
|
||||
.didNotFind("required class", "required classes")
|
||||
.items(Style.QUOTE, missing));
|
||||
}
|
||||
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
|
||||
.found("required class", "required classes").items(Style.QUOTE,
|
||||
getMatches(onClasses, MatchType.PRESENT, classLoader));
|
||||
}
|
||||
List<String> onMissingClasses = getCandidates(metadata,
|
||||
ConditionalOnMissingClass.class);
|
||||
if (onMissingClasses != null) {
|
||||
List<String> present = getMatches(onMissingClasses, MatchType.PRESENT,
|
||||
classLoader);
|
||||
if (!present.isEmpty()) {
|
||||
return ConditionOutcome.noMatch(
|
||||
ConditionMessage.forCondition(ConditionalOnMissingClass.class)
|
||||
.found("unwanted class", "unwanted classes")
|
||||
.items(Style.QUOTE, present));
|
||||
}
|
||||
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
|
||||
.didNotFind("unwanted class", "unwanted classes").items(Style.QUOTE,
|
||||
getMatches(onMissingClasses, MatchType.MISSING, classLoader));
|
||||
}
|
||||
return ConditionOutcome.match(matchMessage);
|
||||
}
|
||||
|
||||
private List<String> getCandidates(AnnotatedTypeMetadata metadata,
|
||||
Class<?> annotationType) {
|
||||
MultiValueMap<String, Object> attributes = metadata
|
||||
.getAllAnnotationAttributes(annotationType.getName(), true);
|
||||
List<String> candidates = new ArrayList<>();
|
||||
if (attributes == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
addAll(candidates, attributes.get("value"));
|
||||
addAll(candidates, attributes.get("name"));
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void addAll(List<String> list, List<Object> itemsToAdd) {
|
||||
if (itemsToAdd != null) {
|
||||
for (Object item : itemsToAdd) {
|
||||
Collections.addAll(list, (String[]) item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
ConditionOutcome[] resolveOutcomes();
|
||||
|
||||
}
|
||||
|
||||
private static final class ThreadedOutcomesResolver implements OutcomesResolver {
|
||||
|
||||
private final Thread thread;
|
||||
|
||||
private volatile ConditionOutcome[] outcomes;
|
||||
|
||||
private ThreadedOutcomesResolver(final OutcomesResolver outcomesResolver) {
|
||||
this.thread = new Thread(
|
||||
() -> this.outcomes = outcomesResolver.resolveOutcomes());
|
||||
this.thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome[] resolveOutcomes() {
|
||||
try {
|
||||
this.thread.join();
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
return this.outcomes;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final class StandardOutcomesResolver implements OutcomesResolver {
|
||||
|
||||
private final String[] autoConfigurationClasses;
|
||||
|
||||
private final int start;
|
||||
|
||||
private final int end;
|
||||
|
||||
private final AutoConfigurationMetadata autoConfigurationMetadata;
|
||||
|
||||
private final ClassLoader beanClassLoader;
|
||||
|
||||
private StandardOutcomesResolver(String[] autoConfigurationClasses, int start,
|
||||
int end, AutoConfigurationMetadata autoConfigurationMetadata,
|
||||
ClassLoader beanClassLoader) {
|
||||
this.autoConfigurationClasses = autoConfigurationClasses;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.autoConfigurationMetadata = autoConfigurationMetadata;
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionOutcome[] resolveOutcomes() {
|
||||
return getOutcomes(this.autoConfigurationClasses, this.start, this.end,
|
||||
this.autoConfigurationMetadata);
|
||||
}
|
||||
|
||||
private ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,
|
||||
int start, int end, AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
ConditionOutcome[] outcomes = new ConditionOutcome[end - start];
|
||||
for (int i = start; i < end; i++) {
|
||||
String autoConfigurationClass = autoConfigurationClasses[i];
|
||||
Set<String> candidates = autoConfigurationMetadata
|
||||
.getSet(autoConfigurationClass, "ConditionalOnClass");
|
||||
if (candidates != null) {
|
||||
outcomes[i - start] = getOutcome(candidates);
|
||||
}
|
||||
}
|
||||
return outcomes;
|
||||
}
|
||||
|
||||
private ConditionOutcome getOutcome(Set<String> candidates) {
|
||||
try {
|
||||
List<String> missing = getMatches(candidates, MatchType.MISSING,
|
||||
this.beanClassLoader);
|
||||
if (!missing.isEmpty()) {
|
||||
return ConditionOutcome.noMatch(
|
||||
ConditionMessage.forCondition(ConditionalOnClass.class)
|
||||
.didNotFind("required class", "required classes")
|
||||
.items(Style.QUOTE, missing));
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// We'll get another chance later
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for a required {@link CloudPlatform}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @see ConditionalOnCloudPlatform
|
||||
*/
|
||||
class OnCloudPlatformCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
Map<String, Object> attributes = metadata
|
||||
.getAnnotationAttributes(ConditionalOnCloudPlatform.class.getName());
|
||||
CloudPlatform cloudPlatform = (CloudPlatform) attributes.get("value");
|
||||
return getMatchOutcome(context.getEnvironment(), cloudPlatform);
|
||||
}
|
||||
|
||||
private ConditionOutcome getMatchOutcome(Environment environment,
|
||||
CloudPlatform cloudPlatform) {
|
||||
String name = cloudPlatform.name();
|
||||
ConditionMessage.Builder message = ConditionMessage
|
||||
.forCondition(ConditionalOnCloudPlatform.class);
|
||||
if (cloudPlatform.isActive(environment)) {
|
||||
return ConditionOutcome.match(message.foundExactly(name));
|
||||
}
|
||||
return ConditionOutcome.noMatch(message.didNotFind(name).atAll());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanExpressionContext;
|
||||
import org.springframework.beans.factory.config.BeanExpressionResolver;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.expression.StandardBeanExpressionResolver;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* A Condition that evaluates a SpEL expression.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @see ConditionalOnExpression
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 20)
|
||||
class OnExpressionCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
String expression = (String) metadata
|
||||
.getAnnotationAttributes(ConditionalOnExpression.class.getName())
|
||||
.get("value");
|
||||
expression = wrapIfNecessary(expression);
|
||||
String rawExpression = expression;
|
||||
expression = context.getEnvironment().resolvePlaceholders(expression);
|
||||
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
|
||||
BeanExpressionResolver resolver = (beanFactory != null)
|
||||
? beanFactory.getBeanExpressionResolver() : null;
|
||||
BeanExpressionContext expressionContext = (beanFactory != null)
|
||||
? new BeanExpressionContext(beanFactory, null) : null;
|
||||
if (resolver == null) {
|
||||
resolver = new StandardBeanExpressionResolver();
|
||||
}
|
||||
Object result = resolver.evaluate(expression, expressionContext);
|
||||
boolean match = result != null && (boolean) result;
|
||||
return new ConditionOutcome(match, ConditionMessage
|
||||
.forCondition(ConditionalOnExpression.class, "(" + rawExpression + ")")
|
||||
.resultedIn(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow user to provide bare expression with no '#{}' wrapper.
|
||||
* @param expression source expression
|
||||
* @return wrapped expression
|
||||
*/
|
||||
private String wrapIfNecessary(String expression) {
|
||||
if (!expression.startsWith("#{")) {
|
||||
return "#{" + expression + "}";
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava.Range;
|
||||
import org.springframework.boot.system.JavaVersion;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for a required version of Java.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Phillip Webb
|
||||
* @see ConditionalOnJava
|
||||
* @since 1.1.0
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 20)
|
||||
class OnJavaCondition extends SpringBootCondition {
|
||||
|
||||
private static final JavaVersion JVM_VERSION = JavaVersion.getJavaVersion();
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
Map<String, Object> attributes = metadata
|
||||
.getAnnotationAttributes(ConditionalOnJava.class.getName());
|
||||
Range range = (Range) attributes.get("range");
|
||||
JavaVersion version = (JavaVersion) attributes.get("value");
|
||||
return getMatchOutcome(range, JVM_VERSION, version);
|
||||
}
|
||||
|
||||
protected ConditionOutcome getMatchOutcome(Range range, JavaVersion runningVersion,
|
||||
JavaVersion version) {
|
||||
boolean match = isWithin(runningVersion, range, version);
|
||||
String expected = String.format(
|
||||
range == Range.EQUAL_OR_NEWER ? "(%s or newer)" : "(older than %s)",
|
||||
version);
|
||||
ConditionMessage message = ConditionMessage
|
||||
.forCondition(ConditionalOnJava.class, expected)
|
||||
.foundExactly(runningVersion);
|
||||
return new ConditionOutcome(match, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the {@code runningVersion} is within the specified range of versions.
|
||||
* @param runningVersion the current version.
|
||||
* @param range the range
|
||||
* @param version the bounds of the range
|
||||
* @return if this version is within the specified range
|
||||
*/
|
||||
private boolean isWithin(JavaVersion runningVersion, Range range,
|
||||
JavaVersion version) {
|
||||
if (range == Range.EQUAL_OR_NEWER) {
|
||||
return runningVersion.isEqualOrNewerThan(version);
|
||||
}
|
||||
if (range == Range.OLDER_THAN) {
|
||||
return runningVersion.isOlderThan(version);
|
||||
}
|
||||
throw new IllegalStateException("Unknown range " + range);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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 javax.naming.NamingException;
|
||||
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.jndi.JndiLocatorDelegate;
|
||||
import org.springframework.jndi.JndiLocatorSupport;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks for JNDI locations.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @since 1.2.0
|
||||
* @see ConditionalOnJndi
|
||||
*/
|
||||
@Order(Ordered.LOWEST_PRECEDENCE - 20)
|
||||
class OnJndiCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(
|
||||
metadata.getAnnotationAttributes(ConditionalOnJndi.class.getName()));
|
||||
String[] locations = annotationAttributes.getStringArray("value");
|
||||
try {
|
||||
return getMatchOutcome(locations);
|
||||
}
|
||||
catch (NoClassDefFoundError ex) {
|
||||
return ConditionOutcome
|
||||
.noMatch(ConditionMessage.forCondition(ConditionalOnJndi.class)
|
||||
.because("JNDI class not found"));
|
||||
}
|
||||
}
|
||||
|
||||
private ConditionOutcome getMatchOutcome(String[] locations) {
|
||||
if (!isJndiAvailable()) {
|
||||
return ConditionOutcome
|
||||
.noMatch(ConditionMessage.forCondition(ConditionalOnJndi.class)
|
||||
.notAvailable("JNDI environment"));
|
||||
}
|
||||
if (locations.length == 0) {
|
||||
return ConditionOutcome.match(ConditionMessage
|
||||
.forCondition(ConditionalOnJndi.class).available("JNDI environment"));
|
||||
}
|
||||
JndiLocator locator = getJndiLocator(locations);
|
||||
String location = locator.lookupFirstLocation();
|
||||
String details = "(" + StringUtils.arrayToCommaDelimitedString(locations) + ")";
|
||||
if (location != null) {
|
||||
return ConditionOutcome
|
||||
.match(ConditionMessage.forCondition(ConditionalOnJndi.class, details)
|
||||
.foundExactly("\"" + location + "\""));
|
||||
}
|
||||
return ConditionOutcome
|
||||
.noMatch(ConditionMessage.forCondition(ConditionalOnJndi.class, details)
|
||||
.didNotFind("any matching JNDI location").atAll());
|
||||
}
|
||||
|
||||
protected boolean isJndiAvailable() {
|
||||
return JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable();
|
||||
}
|
||||
|
||||
protected JndiLocator getJndiLocator(String[] locations) {
|
||||
return new JndiLocator(locations);
|
||||
}
|
||||
|
||||
protected static class JndiLocator extends JndiLocatorSupport {
|
||||
|
||||
private String[] locations;
|
||||
|
||||
public JndiLocator(String[] locations) {
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
public String lookupFirstLocation() {
|
||||
for (String location : this.locations) {
|
||||
try {
|
||||
lookup(location);
|
||||
return location;
|
||||
}
|
||||
catch (NamingException ex) {
|
||||
// Swallow and continue
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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 org.springframework.boot.autoconfigure.condition;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionMessage.Style;
|
||||
import org.springframework.context.annotation.Condition;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationAttributes;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.core.env.PropertyResolver;
|
||||
import org.springframework.core.type.AnnotatedTypeMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* {@link Condition} that checks if properties are defined in environment.
|
||||
*
|
||||
* @author Maciej Walkowiak
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Andy Wilkinson
|
||||
* @since 1.1.0
|
||||
* @see ConditionalOnProperty
|
||||
*/
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE + 40)
|
||||
class OnPropertyCondition extends SpringBootCondition {
|
||||
|
||||
@Override
|
||||
public ConditionOutcome getMatchOutcome(ConditionContext context,
|
||||
AnnotatedTypeMetadata metadata) {
|
||||
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
|
||||
metadata.getAllAnnotationAttributes(
|
||||
ConditionalOnProperty.class.getName()));
|
||||
List<ConditionMessage> noMatch = new ArrayList<>();
|
||||
List<ConditionMessage> match = new ArrayList<>();
|
||||
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
|
||||
ConditionOutcome outcome = determineOutcome(annotationAttributes,
|
||||
context.getEnvironment());
|
||||
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
|
||||
}
|
||||
if (!noMatch.isEmpty()) {
|
||||
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
|
||||
}
|
||||
return ConditionOutcome.match(ConditionMessage.of(match));
|
||||
}
|
||||
|
||||
private List<AnnotationAttributes> annotationAttributesFromMultiValueMap(
|
||||
MultiValueMap<String, Object> multiValueMap) {
|
||||
List<Map<String, Object>> maps = new ArrayList<>();
|
||||
for (Entry<String, List<Object>> entry : multiValueMap.entrySet()) {
|
||||
for (int i = 0; i < entry.getValue().size(); i++) {
|
||||
Map<String, Object> map;
|
||||
if (i < maps.size()) {
|
||||
map = maps.get(i);
|
||||
}
|
||||
else {
|
||||
map = new HashMap<>();
|
||||
maps.add(map);
|
||||
}
|
||||
map.put(entry.getKey(), entry.getValue().get(i));
|
||||
}
|
||||
}
|
||||
List<AnnotationAttributes> annotationAttributes = new ArrayList<>(maps.size());
|
||||
for (Map<String, Object> map : maps) {
|
||||
annotationAttributes.add(AnnotationAttributes.fromMap(map));
|
||||
}
|
||||
return annotationAttributes;
|
||||
}
|
||||
|
||||
private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes,
|
||||
PropertyResolver resolver) {
|
||||
Spec spec = new Spec(annotationAttributes);
|
||||
List<String> missingProperties = new ArrayList<>();
|
||||
List<String> nonMatchingProperties = new ArrayList<>();
|
||||
spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
|
||||
if (!missingProperties.isEmpty()) {
|
||||
return ConditionOutcome.noMatch(
|
||||
ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
|
||||
.didNotFind("property", "properties")
|
||||
.items(Style.QUOTE, missingProperties));
|
||||
}
|
||||
if (!nonMatchingProperties.isEmpty()) {
|
||||
return ConditionOutcome.noMatch(
|
||||
ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
|
||||
.found("different value in property",
|
||||
"different value in properties")
|
||||
.items(Style.QUOTE, nonMatchingProperties));
|
||||
}
|
||||
return ConditionOutcome.match(ConditionMessage
|
||||
.forCondition(ConditionalOnProperty.class, spec).because("matched"));
|
||||
}
|
||||
|
||||
private static class Spec {
|
||||
|
||||
private final String prefix;
|
||||
|
||||
private final String havingValue;
|
||||
|
||||
private final String[] names;
|
||||
|
||||
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.matchIfMissing = annotationAttributes.getBoolean("matchIfMissing");
|
||||
}
|
||||
|
||||
private String[] getNames(Map<String, Object> annotationAttributes) {
|
||||
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 void collectProperties(PropertyResolver resolver, List<String> missing,
|
||||
List<String> nonMatching) {
|
||||
for (String name : this.names) {
|
||||
String key = this.prefix + name;
|
||||
if (resolver.containsProperty(key)) {
|
||||
if (!isMatch(resolver.getProperty(key), this.havingValue)) {
|
||||
nonMatching.add(name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!this.matchIfMissing) {
|
||||
missing.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isMatch(String value, String requiredValue) {
|
||||
if (StringUtils.hasLength(requiredValue)) {
|
||||
return requiredValue.equalsIgnoreCase(value);
|
||||
}
|
||||
return !"false".equalsIgnoreCase(value);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user