Skip to content

Migrate DependencyTests to ArchUnit #2706

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>3.0.0-SNAPSHOT</version>
<version>3.0.0-dependencies-SNAPSHOT</version>

<name>Spring Data Core</name>
<description>Core Spring concepts underpinning every Spring Data module.</description>
Expand All @@ -32,6 +32,7 @@

<scala>2.11.7</scala>
<xmlbeam>1.4.24</xmlbeam>
<archunit.version>1.0.0</archunit.version>
<java-module-name>spring.data.commons</java-module-name>

</properties>
Expand Down Expand Up @@ -334,20 +335,20 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>de.schauderhaft.degraph</groupId>
<artifactId>degraph-check</artifactId>
<version>0.1.4</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jmolecules.integrations</groupId>
<artifactId>jmolecules-spring</artifactId>
<version>${jmolecules-integration}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit</artifactId>
<version>${archunit.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2022 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
*
* https://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.querydsl;

import com.querydsl.core.types.Predicate;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.io.InputStreamSource;
import org.springframework.data.domain.Example;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.data.repository.core.support.RepositoryFragment;
import org.springframework.data.repository.core.support.RepositoryFragmentsFactoryBean;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.repository.query.FluentQuery.ReactiveFluentQuery;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;

import java.util.Arrays;
import java.util.Properties;

/**
* {@link RuntimeHintsRegistrar} holding required hints to bootstrap Querydsl repositories. <br />
* Already registered via {@literal aot.factories}.
*
* @author Christoph Strobl
* @author Mark Paluch
* @since 3.0
*/
class QuerydslRepositoryRuntimeHints implements RuntimeHintsRegistrar {

private static final boolean PROJECT_REACTOR_PRESENT = ClassUtils.isPresent("reactor.core.publisher.Flux",
QuerydslRepositoryRuntimeHints.class.getClassLoader());

@Override
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {

if (QuerydslUtils.QUERY_DSL_PRESENT) {

// repository infrastructure
hints.reflection().registerTypes(Arrays.asList( //
TypeReference.of(Predicate.class), //
TypeReference.of(QuerydslPredicateExecutor.class)), builder -> {
builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
});

if (PROJECT_REACTOR_PRESENT) {
// repository infrastructure
hints.reflection().registerTypes(Arrays.asList( //
TypeReference.of(ReactiveQuerydslPredicateExecutor.class)), builder -> {
builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
});
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2022 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
*
* https://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.aot;

import org.springframework.data.repository.config.RepositoryConfigurationPostProcessor;

/**
* default post processing to be applied when no other {@link RepositoryConfigurationPostProcessor} is applied.
*
* @author Mark Paluch
* @author Jens Schauder
* @since 3.0
*/
class FallbackRepositoryAotConfigurationPostProcessor extends RepositoryRegistrationAotProcessor implements RepositoryConfigurationPostProcessor.FallbackRepositoryConfigurationPostProcessor {
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.springframework.data.aot.TypeContributor;
import org.springframework.data.repository.config.RepositoryConfiguration;
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
import org.springframework.data.repository.config.RepositoryConfigurationPostProcessor;
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
Expand All @@ -56,16 +57,14 @@
* provide custom logic for contributing additional (eg. reflection) configuration. By default, reflection configuration
* will be added for types reachable from the repository declaration and query methods as well as all used
* {@link Annotation annotations} from the {@literal org.springframework.data} namespace.
* </p>
* The processor is typically configured via {@link RepositoryConfigurationExtension#getRepositoryAotProcessor()} and
* gets added by the {@link org.springframework.data.repository.config.RepositoryConfigurationDelegate}.

*
* @author Christoph Strobl
* @author John Blum
* @since 3.0
*/
@SuppressWarnings("unused")
public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotProcessor, BeanFactoryAware {
public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotProcessor, BeanFactoryAware, RepositoryConfigurationPostProcessor {

private ConfigurableListableBeanFactory beanFactory;

Expand Down Expand Up @@ -117,6 +116,7 @@ protected ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}

@Override
public void setConfigMap(@Nullable Map<String, RepositoryConfiguration<?>> configMap) {
this.configMap = configMap;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,24 +87,6 @@ public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader)
});
}

if (QuerydslUtils.QUERY_DSL_PRESENT) {

// repository infrastructure
hints.reflection().registerTypes(Arrays.asList( //
TypeReference.of(Predicate.class), //
TypeReference.of(QuerydslPredicateExecutor.class)), builder -> {
builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
});

if (PROJECT_REACTOR_PRESENT) {
// repository infrastructure
hints.reflection().registerTypes(Arrays.asList( //
TypeReference.of(ReactiveQuerydslPredicateExecutor.class)), builder -> {
builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS);
});
}
}

// named queries
hints.reflection().registerTypes(Arrays.asList( //
TypeReference.of(Properties.class), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.springframework.core.log.LogMessage;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.metrics.StartupStep;
import org.springframework.data.repository.config.RepositoryConfigurationPostProcessor.FallbackRepositoryConfigurationPostProcessor;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
Expand Down Expand Up @@ -83,11 +84,11 @@ public class RepositoryConfigurationDelegate {
* {@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}.
* @param resourceLoader must not be {@literal null}.
* @param environment must not be {@literal null}.
*/
public RepositoryConfigurationDelegate(RepositoryConfigurationSource configurationSource,
ResourceLoader resourceLoader, Environment environment) {
ResourceLoader resourceLoader, Environment environment) {

this.isXml = configurationSource instanceof XmlRepositoryConfigurationSource;
boolean isAnnotation = configurationSource instanceof AnnotationRepositoryConfigurationSource;
Expand All @@ -106,13 +107,13 @@ public RepositoryConfigurationDelegate(RepositoryConfigurationSource configurati
* 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 environment can be {@literal null}.
* @param resourceLoader can be {@literal null}.
* @return the given {@link Environment} if not {@literal null}, a configured {@link Environment}, or a default
* {@link Environment}.
* {@link Environment}.
*/
private static Environment defaultEnvironment(@Nullable Environment environment,
@Nullable ResourceLoader resourceLoader) {
@Nullable ResourceLoader resourceLoader) {

if (environment != null) {
return environment;
Expand All @@ -125,14 +126,14 @@ private static Environment defaultEnvironment(@Nullable Environment environment,
/**
* Registers the discovered repositories in the given {@link BeanDefinitionRegistry}.
*
* @param registry {@link BeanDefinitionRegistry} in which to register the repository bean.
* @param registry {@link BeanDefinitionRegistry} in which to register the repository bean.
* @param extension {@link RepositoryConfigurationExtension} for the module.
* @return {@link BeanComponentDefinition}s for all repository bean definitions found.
* @see org.springframework.data.repository.config.RepositoryConfigurationExtension
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
*/
public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegistry registry,
RepositoryConfigurationExtension extension) {
RepositoryConfigurationExtension extension) {

if (logger.isInfoEnabled()) {
logger.info(LogMessage.format("Bootstrapping Spring Data %s repositories in %s mode.", //
Expand Down Expand Up @@ -210,29 +211,52 @@ public List<BeanComponentDefinition> registerRepositoriesIn(BeanDefinitionRegist
watch.getLastTaskTimeMillis(), configurations.size(), extension.getModuleName()));
}

// TODO: AOT Processing -> guard this one with a flag so it's not always present
// TODO: With regard to AOT Processing, perhaps we need to be smart and detect whether "core" AOT components are
// (or rather configuration is) present on the classpath to enable Spring Data AOT component registration.
registerAotComponents(registry, extension, metadataByRepositoryBeanName);
registerPostProcessorComponents(registry, extension, metadataByRepositoryBeanName);

return definitions;
}

private void registerAotComponents(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension,
Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName) {
private void registerPostProcessorComponents(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension,
Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName) {

// module-specific repository aot processor
String repositoryAotProcessorBeanName = String.format("data-%s.repository-aot-processor" /* might be duplicate */,
extension.getModuleIdentifier());
boolean hasPostProcessors = false;
List<RepositoryConfigurationPostProcessor> processors = SpringFactoriesLoader.loadFactories(RepositoryConfigurationPostProcessor.class, resourceLoader.getClassLoader());
for (RepositoryConfigurationPostProcessor processor : processors) {

if (!registry.isBeanNameInUse(repositoryAotProcessorBeanName)) {
if (!processor.supports(extension)) {
continue;
}

hasPostProcessors = true;
registerRepositoryPostProcessor(registry, extension, processor.getClass().getName(), metadataByRepositoryBeanName);
}

if (!hasPostProcessors) {

BeanDefinitionBuilder repositoryAotProcessor = BeanDefinitionBuilder
.rootBeanDefinition(extension.getRepositoryAotProcessor()).setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
List<FallbackRepositoryConfigurationPostProcessor> fallback = SpringFactoriesLoader.loadFactories(FallbackRepositoryConfigurationPostProcessor.class, resourceLoader.getClassLoader());

repositoryAotProcessor.addPropertyValue("configMap", metadataByRepositoryBeanName);
for (RepositoryConfigurationPostProcessor processor : fallback) {

registry.registerBeanDefinition(repositoryAotProcessorBeanName, repositoryAotProcessor.getBeanDefinition());
if (!processor.supports(extension)) {
continue;
}

registerRepositoryPostProcessor(registry, extension, processor.getClass().getName(), metadataByRepositoryBeanName);
}
}
}

private static void registerRepositoryPostProcessor(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension, String className, Map<String, RepositoryConfigurationAdapter<?>> metadataByRepositoryBeanName) {

String repositoryProcessorBeanName = String.format("data-%s.repository-post-processor.%s",
extension.getModuleIdentifier(), className);

if (!registry.isBeanNameInUse(repositoryProcessorBeanName)) {
BeanDefinitionBuilder repositoryPostProcessor = BeanDefinitionBuilder
.rootBeanDefinition(className).setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

repositoryPostProcessor.addPropertyValue("configMap", metadataByRepositoryBeanName);
registry.registerBeanDefinition(repositoryProcessorBeanName, repositoryPostProcessor.getBeanDefinition());
}
}

Expand All @@ -242,10 +266,10 @@ private void registerAotComponents(BeanDefinitionRegistry registry, RepositoryCo
* augment the {@link LazyRepositoryInjectionPointResolver}'s configuration if there already is one configured.
*
* @param configurations must not be {@literal null}.
* @param registry must not be {@literal null}.
* @param registry must not be {@literal null}.
*/
private static void potentiallyLazifyRepositories(Map<String, RepositoryConfiguration<?>> configurations,
BeanDefinitionRegistry registry, BootstrapMode mode) {
BeanDefinitionRegistry registry, BootstrapMode mode) {

if (!DefaultListableBeanFactory.class.isInstance(registry) || BootstrapMode.DEFAULT.equals(mode)) {
return;
Expand Down Expand Up @@ -283,7 +307,7 @@ private static void potentiallyLazifyRepositories(Map<String, RepositoryConfigur
* scanning.
*
* @return {@literal true} if multiple data store repository implementations are present in the application. This
* typically means an Spring application is using more than 1 type of data store.
* typically means an Spring application is using more than 1 type of data store.
*/
private boolean multipleStoresDetected() {

Expand Down Expand Up @@ -333,7 +357,7 @@ public LazyRepositoryInjectionPointResolver(Map<String, RepositoryConfiguration<
*
* @param configurations must not be {@literal null}.
* @return a new {@link LazyRepositoryInjectionPointResolver} that will have its configurations augmented with the
* given ones.
* given ones.
*/
LazyRepositoryInjectionPointResolver withAdditionalConfigurations(
Map<String, RepositoryConfiguration<?>> configurations) {
Expand Down
Loading