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:
Phillip Webb
2017-09-19 14:29:46 -07:00
parent 0419d42b7c
commit 0ba4830b4f
4023 changed files with 0 additions and 0 deletions

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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 {
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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));
}
}

View File

@@ -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 {};
}

View File

@@ -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 {};
}

View File

@@ -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;
}

View File

@@ -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();
}
}
}

View File

@@ -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
}

View File

@@ -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 {};
}

View File

@@ -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 {};
}

View File

@@ -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
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 {};
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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 {
}
}

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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;

View File

@@ -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 {
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}
}
}

View File

@@ -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"));
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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");
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
}

View File

@@ -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"));
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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));
}
}
}

View File

@@ -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);
* }
*
* &#064;ConditionalOnJndi()
* static class OnJndi {
* }
*
* &#064;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();
}
}

View File

@@ -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);
* }
*
* &#064;ConditionalOnJndi()
* static class OnJndi {
* }
*
* &#064;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));
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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());
}
}

View File

@@ -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">
* &#064;Configuration
* public class MyAutoConfiguration {
*
* &#064;ConditionalOnBean
* &#064;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;
}

View File

@@ -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 {};
}

View File

@@ -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();
}

View File

@@ -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";
}

View File

@@ -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
}
}

View File

@@ -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 {};
}

View File

@@ -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">
* &#064;Configuration
* public class MyAutoConfiguration {
*
* &#064;ConditionalOnMissingBean
* &#064;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;
}

View File

@@ -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 {};
}

View File

@@ -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 {
}

View File

@@ -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;
}

View File

@@ -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 {};
}

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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);
* }
*
* &#064;ConditionalOnJndi()
* static class OnJndi {
* }
*
* &#064;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));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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());
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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