/*
* Copyright 2014-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.data.repository.config;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.util.Assert;
/**
* Delegate for configuration integration to reuse the general way of detecting repositories. Customization is done by
* providing a configuration format specific {@link RepositoryConfigurationSource} (currently either XML or annotations
* are supported). The actual registration can then be triggered for different {@link RepositoryConfigurationExtension}
* s.
*
* @author Oliver Gierke
*/
public class RepositoryConfigurationDelegate {
private static final Logger LOGGER = LoggerFactory.getLogger(RepositoryConfigurationDelegate.class);
private static final String REPOSITORY_REGISTRATION = "Spring Data {} - Registering repository: {} - Interface: {} - Factory: {}";
private static final String MULTIPLE_MODULES = "Multiple Spring Data modules found, entering strict repository configuration mode!";
static final String FACTORY_BEAN_OBJECT_TYPE = "factoryBeanObjectType";
private final RepositoryConfigurationSource configurationSource;
private final ResourceLoader resourceLoader;
private final Environment environment;
private final BeanNameGenerator beanNameGenerator;
private final boolean isXml;
private final boolean inMultiStoreMode;
/**
* Creates a new {@link RepositoryConfigurationDelegate} for the given {@link RepositoryConfigurationSource} and
* {@link ResourceLoader} and {@link Environment}.
*
* @param configurationSource must not be {@literal null}.
* @param resourceLoader must not be {@literal null}.
* @param environment must not be {@literal null}.
*/
public RepositoryConfigurationDelegate(RepositoryConfigurationSource configurationSource,
ResourceLoader resourceLoader, Environment environment) {
this.isXml = configurationSource instanceof XmlRepositoryConfigurationSource;
boolean isAnnotation = configurationSource instanceof AnnotationRepositoryConfigurationSource;
Assert.isTrue(isXml || isAnnotation,
"Configuration source must either be an Xml- or an AnnotationBasedConfigurationSource!");
Assert.notNull(resourceLoader, "ResourceLoader must not be null!");
RepositoryBeanNameGenerator generator = new RepositoryBeanNameGenerator();
generator.setBeanClassLoader(resourceLoader.getClassLoader());
this.beanNameGenerator = generator;
this.configurationSource = configurationSource;
this.resourceLoader = resourceLoader;
this.environment = defaultEnvironment(environment, resourceLoader);
this.inMultiStoreMode = multipleStoresDetected();
}
/**
* Defaults the environment in case the given one is null. Used as fallback, in case the legacy constructor was
* invoked.
*
* @param environment can be {@literal null}.
* @param resourceLoader can be {@literal null}.
* @return
*/
private static Environment defaultEnvironment(Environment environment, ResourceLoader resourceLoader) {
if (environment != null) {
return environment;
}
return resourceLoader instanceof EnvironmentCapable ? ((EnvironmentCapable) resourceLoader).getEnvironment()
: new StandardEnvironment();
}
/**
* Registers the found repositories in the given {@link BeanDefinitionRegistry}.
*
* @param registry
* @param extension
* @return {@link BeanComponentDefinition}s for all repository bean definitions found.
*/
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
extension.registerBeansForRoot(registry, configurationSource);
RepositoryBeanDefinitionBuilder builder = new RepositoryBeanDefinitionBuilder(registry, extension, resourceLoader,
environment);
List<BeanComponentDefinition> definitions = new ArrayList<>();
for (RepositoryConfiguration<? extends RepositoryConfigurationSource> configuration : extension
.getRepositoryConfigurations(configurationSource, resourceLoader, inMultiStoreMode)) {
BeanDefinitionBuilder definitionBuilder = builder.build(configuration);
extension.postProcess(definitionBuilder, configurationSource);
if (isXml) {
extension.postProcess(definitionBuilder, (XmlRepositoryConfigurationSource) configurationSource);
} else {
extension.postProcess(definitionBuilder, (AnnotationRepositoryConfigurationSource) configurationSource);
}
AbstractBeanDefinition beanDefinition = definitionBuilder.getBeanDefinition();
String beanName = beanNameGenerator.generateBeanName(beanDefinition, registry);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(REPOSITORY_REGISTRATION, extension.getModuleName(), beanName,
configuration.getRepositoryInterface(), configuration.getRepositoryFactoryBeanClassName());
}
beanDefinition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, configuration.getRepositoryInterface());
registry.registerBeanDefinition(beanName, beanDefinition);
definitions.add(new BeanComponentDefinition(beanDefinition, beanName));
}
return definitions;
}
/**
* Scans {@code repository.support} packages for implementations of {@link RepositoryFactorySupport}. Finding more
* than a single type is considered a multi-store configuration scenario which will trigger stricter repository
* scanning.
*
* @return
*/
private boolean multipleStoresDetected() {
boolean multipleModulesFound = SpringFactoriesLoader
.loadFactoryNames(RepositoryFactorySupport.class, resourceLoader.getClassLoader()).size() > 1;
if (multipleModulesFound) {
LOGGER.info(MULTIPLE_MODULES);
}
return multipleModulesFound;
}
}