package org.springframework.data.repository.core.support;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
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.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
import org.springframework.data.repository.query.ExtensionAwareQueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.util.Lazy;
import org.springframework.util.Assert;
public abstract class RepositoryFactoryBeanSupport<T extends Repository<S, ID>, S, ID>
implements InitializingBean, RepositoryFactoryInformation<S, ID>, FactoryBean<T>, BeanClassLoaderAware,
BeanFactoryAware, ApplicationEventPublisherAware {
private final Class<? extends T> repositoryInterface;
private RepositoryFactorySupport factory;
private Key queryLookupStrategyKey;
private Optional<Class<?>> repositoryBaseClass = Optional.empty();
private Optional<Object> customImplementation = Optional.empty();
private Optional<RepositoryFragments> repositoryFragments = Optional.empty();
private NamedQueries namedQueries;
private Optional<MappingContext<?, ?>> mappingContext = Optional.empty();
private ClassLoader classLoader;
private BeanFactory beanFactory;
private boolean lazyInit = false;
private Optional<QueryMethodEvaluationContextProvider> evaluationContextProvider = Optional.empty();
private ApplicationEventPublisher publisher;
private Lazy<T> repository;
private RepositoryMetadata repositoryMetadata;
protected RepositoryFactoryBeanSupport(Class<? extends T> repositoryInterface) {
Assert.notNull(repositoryInterface, "Repository interface must not be null!");
this.repositoryInterface = repositoryInterface;
}
public void setRepositoryBaseClass(Class<?> repositoryBaseClass) {
this.repositoryBaseClass = Optional.ofNullable(repositoryBaseClass);
}
public void setQueryLookupStrategyKey(Key queryLookupStrategyKey) {
this.queryLookupStrategyKey = queryLookupStrategyKey;
}
public void setCustomImplementation(Object customImplementation) {
this.customImplementation = Optional.ofNullable(customImplementation);
}
public void setRepositoryFragments(RepositoryFragments repositoryFragments) {
this.repositoryFragments = Optional.ofNullable(repositoryFragments);
}
public void setNamedQueries(NamedQueries namedQueries) {
this.namedQueries = namedQueries;
}
protected void setMappingContext(MappingContext<?, ?> mappingContext) {
this.mappingContext = Optional.ofNullable(mappingContext);
}
public void setEvaluationContextProvider(QueryMethodEvaluationContextProvider evaluationContextProvider) {
this.evaluationContextProvider = Optional.of(evaluationContextProvider);
}
public void setLazyInit(boolean lazy) {
this.lazyInit = lazy;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
if (!this.evaluationContextProvider.isPresent() && ListableBeanFactory.class.isInstance(beanFactory)) {
this.evaluationContextProvider = Optional
.of(new ExtensionAwareQueryMethodEvaluationContextProvider((ListableBeanFactory) beanFactory));
}
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@SuppressWarnings("unchecked")
public EntityInformation<S, ID> getEntityInformation() {
return (EntityInformation<S, ID>) factory.getEntityInformation(repositoryMetadata.getDomainType());
}
public RepositoryInformation getRepositoryInformation() {
RepositoryFragments fragments = customImplementation.map(RepositoryFragments::just)
.orElse(RepositoryFragments.empty());
return factory.getRepositoryInformation(repositoryMetadata, fragments);
}
public PersistentEntity<?, ?> getPersistentEntity() {
return mappingContext.orElseThrow(() -> new IllegalStateException("No MappingContext available!"))
.getRequiredPersistentEntity(repositoryMetadata.getDomainType());
}
public List<QueryMethod> getQueryMethods() {
return factory.getQueryMethods();
}
@Nonnull
public T getObject() {
return this.repository.get();
}
@Nonnull
public Class<? extends T> getObjectType() {
return repositoryInterface;
}
public boolean isSingleton() {
return true;
}
public void afterPropertiesSet() {
this.factory = createRepositoryFactory();
this.factory.setQueryLookupStrategyKey(queryLookupStrategyKey);
this.factory.setNamedQueries(namedQueries);
this.factory.setEvaluationContextProvider(
evaluationContextProvider.orElseGet(() -> QueryMethodEvaluationContextProvider.DEFAULT));
this.factory.setBeanClassLoader(classLoader);
this.factory.setBeanFactory(beanFactory);
if (publisher != null) {
this.factory.addRepositoryProxyPostProcessor(new EventPublishingRepositoryProxyPostProcessor(publisher));
}
repositoryBaseClass.ifPresent(this.factory::setRepositoryBaseClass);
RepositoryFragments customImplementationFragment = customImplementation
.map(RepositoryFragments::just)
.orElseGet(RepositoryFragments::empty);
RepositoryFragments repositoryFragmentsToUse = this.repositoryFragments
.orElseGet(RepositoryFragments::empty)
.append(customImplementationFragment);
this.repositoryMetadata = this.factory.getRepositoryMetadata(repositoryInterface);
this.mappingContext.ifPresent(it -> it.getPersistentEntity(repositoryMetadata.getDomainType()));
this.repository = Lazy.of(() -> this.factory.getRepository(repositoryInterface, repositoryFragmentsToUse));
if (!lazyInit) {
this.repository.get();
}
}
protected abstract RepositoryFactorySupport createRepositoryFactory();
}