GH-14 - Remove Lombok from production sources.

Polished a lot of Javadoc.
This commit is contained in:
Oliver Drotbohm
2023-01-12 00:54:04 +01:00
parent 20554c3af3
commit 9ce6bf23ae
87 changed files with 3546 additions and 1061 deletions

View File

@@ -15,9 +15,6 @@
*/
package org.springframework.modulith.test;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -35,8 +32,8 @@ import org.springframework.test.context.TestConstructor.AutowireMode;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Bootstraps the module containing the package of the test class annotated with {@link ApplicationModuleTest}. Will apply the
* following modifications to the Spring Boot configuration:
* Bootstraps the module containing the package of the test class annotated with {@link ApplicationModuleTest}. Will
* apply the following modifications to the Spring Boot configuration:
* <ul>
* <li>Restricts the component scanning to the module's package.
* <li>
@@ -76,7 +73,6 @@ public @interface ApplicationModuleTest {
*/
String[] extraIncludes() default {};
@RequiredArgsConstructor
public enum BootstrapMode {
/**
@@ -94,6 +90,19 @@ public @interface ApplicationModuleTest {
*/
ALL_DEPENDENCIES(DependencyDepth.ALL);
private final @Getter DependencyDepth depth;
private final DependencyDepth depth;
private BootstrapMode(DependencyDepth depth) {
this.depth = depth;
}
/**
* Returns the {@link DependencyDepth} associated with the {@link BootstrapMode}.
*
* @return will never be {@literal null}.
*/
public DependencyDepth getDepth() {
return depth;
}
}
}

View File

@@ -15,21 +15,31 @@
*/
package org.springframework.modulith.test;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.util.Assert;
/**
* Default implementation of {@link AssertablePublishedEvents}.
*
* @author Oliver Drotbohm
*/
@RequiredArgsConstructor
class DefaultAssertablePublishedEvents implements AssertablePublishedEvents, ApplicationListener<ApplicationEvent> {
private final DefaultPublishedEvents delegate;
/**
* Creates a new {@link DefaultAssertablePublishedEvents} with the given {@link DefaultPublishedEvents} delegate.
*
* @param delegate must not be {@literal null}.
*/
DefaultAssertablePublishedEvents(DefaultPublishedEvents delegate) {
Assert.notNull(delegate, "DefaultPublishedEvents must not be null!");
this.delegate = delegate;
}
/**
* Creates a new {@link DefaultAssertablePublishedEvents}.
*/

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.modulith.test;
import lombok.RequiredArgsConstructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -88,11 +86,14 @@ class DefaultPublishedEvents implements PublishedEvents, ApplicationListener<App
: source;
}
@RequiredArgsConstructor(staticName = "of")
private static class SimpleTypedPublishedEvents<T> implements TypedPublishedEvents<T> {
private final List<T> events;
private SimpleTypedPublishedEvents(List<T> events) {
this.events = events;
}
private static <T> SimpleTypedPublishedEvents<T> of(Stream<T> stream) {
return new SimpleTypedPublishedEvents<>(stream.toList());
}

View File

@@ -15,17 +15,15 @@
*/
package org.springframework.modulith.test;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.modulith.model.ApplicationModule;
@@ -47,16 +45,14 @@ class ModuleContextCustomizerFactory implements ContextCustomizerFactory {
public ContextCustomizer createContextCustomizer(Class<?> testClass,
List<ContextConfigurationAttributes> configAttributes) {
ApplicationModuleTest moduleTest = AnnotatedElementUtils.getMergedAnnotation(testClass,
ApplicationModuleTest.class);
var moduleTest = AnnotatedElementUtils.getMergedAnnotation(testClass, ApplicationModuleTest.class);
return moduleTest == null ? null : new ModuleContextCustomizer(testClass);
}
@Slf4j
@EqualsAndHashCode
static class ModuleContextCustomizer implements ContextCustomizer {
private static final Logger LOGGER = LoggerFactory.getLogger(ModuleContextCustomizer.class);
private static final String BEAN_NAME = ModuleTestExecution.class.getName();
private final Supplier<ModuleTestExecution> execution;
@@ -72,14 +68,14 @@ class ModuleContextCustomizerFactory implements ContextCustomizerFactory {
@Override
public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) {
ModuleTestExecution testExecution = execution.get();
var testExecution = execution.get();
logModules(testExecution);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
var beanFactory = context.getBeanFactory();
beanFactory.registerSingleton(BEAN_NAME, testExecution);
DefaultPublishedEvents events = new DefaultPublishedEvents();
var events = new DefaultPublishedEvents();
beanFactory.registerSingleton(events.getClass().getName(), events);
context.addApplicationListener(events);
}
@@ -94,48 +90,47 @@ class ModuleContextCustomizerFactory implements ContextCustomizerFactory {
var message = "Bootstrapping @%s for %s in mode %s (%s)…"
.formatted(ApplicationModuleTest.class.getName(), moduleName, bootstrapMode, modules.getModulithSource());
LOG.info(message);
LOG.info("");
LOGGER.info(message);
LOGGER.info("");
Arrays.stream(module.toString(modules).split("\n")).forEach(LOG::info);
Arrays.stream(module.toString(modules).split("\n")).forEach(LOGGER::info);
List<ApplicationModule> extraIncludes = execution.getExtraIncludes();
var extraIncludes = execution.getExtraIncludes();
if (!extraIncludes.isEmpty()) {
logHeadline("Extra includes:");
LOG.info("> " + extraIncludes.stream().map(ApplicationModule::getName).collect(Collectors.joining(", ")));
LOGGER.info("> " + extraIncludes.stream().map(ApplicationModule::getName).collect(Collectors.joining(", ")));
}
Set<ApplicationModule> sharedModules = modules.getSharedModules();
var sharedModules = modules.getSharedModules();
if (!sharedModules.isEmpty()) {
logHeadline("Shared modules:");
LOG.info("> " + sharedModules.stream().map(ApplicationModule::getName).collect(Collectors.joining(", ")));
LOGGER.info("> " + sharedModules.stream().map(ApplicationModule::getName).collect(Collectors.joining(", ")));
}
List<ApplicationModule> dependencies = execution.getDependencies();
var dependencies = execution.getDependencies();
if (!dependencies.isEmpty() || !sharedModules.isEmpty()) {
logHeadline("Included dependencies:");
Stream<ApplicationModule> dependenciesPlusMissingSharedOnes = //
Stream.concat(dependencies.stream(), sharedModules.stream() //
.filter(it -> !dependencies.contains(it)));
var dependenciesPlusMissingSharedOnes = Stream.concat(dependencies.stream(), sharedModules.stream() //
.filter(it -> !dependencies.contains(it)));
dependenciesPlusMissingSharedOnes //
.map(it -> it.toString(modules)) //
.forEach(it -> {
LOG.info("");
Arrays.stream(it.split("\n")).forEach(LOG::info);
LOGGER.info("");
Arrays.stream(it.split("\n")).forEach(LOGGER::info);
});
}
LOG.info("");
LOGGER.info("");
}
private static void logHeadline(String headline) {
@@ -144,9 +139,36 @@ class ModuleContextCustomizerFactory implements ContextCustomizerFactory {
private static void logHeadline(String headline, Runnable additional) {
LOG.info("");
LOG.info(headline);
LOGGER.info("");
LOGGER.info(headline);
additional.run();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ModuleContextCustomizer that)) {
return false;
}
return Objects.equals(execution, that.execution);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hash(execution);
}
}
}

View File

@@ -15,16 +15,14 @@
*/
package org.springframework.modulith.test;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
@@ -43,7 +41,7 @@ import org.springframework.util.StringUtils;
*
* @author Oliver Drotbohm
*/
@Configuration
@Configuration(proxyBeanMethods = false)
@Import(ModuleTestAutoConfiguration.AutoConfigurationAndEntityScanPackageCustomizer.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
class ModuleTestAutoConfiguration {
@@ -51,9 +49,10 @@ class ModuleTestAutoConfiguration {
private static final String AUTOCONFIG_PACKAGES = "org.springframework.boot.autoconfigure.AutoConfigurationPackages";
private static final String ENTITY_SCAN_PACKAGE = "org.springframework.boot.autoconfigure.domain.EntityScanPackages";
@Slf4j
static class AutoConfigurationAndEntityScanPackageCustomizer implements ImportBeanDefinitionRegistrar {
private static final Logger LOGGER = LoggerFactory.getLogger(AutoConfigurationAndEntityScanPackageCustomizer.class);
/*
* (non-Javadoc)
* @see org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
@@ -61,10 +60,10 @@ class ModuleTestAutoConfiguration {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ModuleTestExecution execution = ((BeanFactory) registry).getBean(ModuleTestExecution.class);
List<String> basePackages = execution.getBasePackages().toList();
var execution = ((BeanFactory) registry).getBean(ModuleTestExecution.class);
var basePackages = execution.getBasePackages().toList();
LOG.info("Re-configuring auto-configuration and entity scan packages to: {}.",
LOGGER.info("Re-configuring auto-configuration and entity scan packages to: {}.",
StringUtils.collectionToDelimitedString(basePackages, ", "));
setBasePackagesOn(registry, AUTOCONFIG_PACKAGES, "BasePackagesBeanDefinition", "basePackages", basePackages);
@@ -80,10 +79,10 @@ class ModuleTestAutoConfiguration {
return;
}
BeanDefinition definition = registry.getBeanDefinition(beanName);
var definition = registry.getBeanDefinition(beanName);
// For Boot 2.4, we deal with a BasePackagesBeanDefinition
Field field = Arrays.stream(definition.getClass().getDeclaredFields())
var field = Arrays.stream(definition.getClass().getDeclaredFields())
.filter(__ -> definition.getClass().getSimpleName().equals(definitionType))
.filter(it -> it.getName().equals(fieldName))
.findFirst()

View File

@@ -15,20 +15,16 @@
*/
package org.springframework.modulith.test;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.AnnotatedClassFinder;
import org.springframework.core.annotation.AnnotatedElementUtils;
@@ -43,26 +39,26 @@ import com.tngtech.archunit.thirdparty.com.google.common.base.Suppliers;
/**
* @author Oliver Drotbohm
*/
@Slf4j
@EqualsAndHashCode(of = "key")
public class ModuleTestExecution implements Iterable<ApplicationModule> {
private static final Logger LOGGER = LoggerFactory.getLogger(ModuleTestExecution.class);
private static Map<Class<?>, Class<?>> MODULITH_TYPES = new HashMap<>();
private static Map<Key, ModuleTestExecution> EXECUTIONS = new HashMap<>();
private final Key key;
private final @Getter BootstrapMode bootstrapMode;
private final @Getter ApplicationModule module;
private final @Getter ApplicationModules modules;
private final @Getter List<ApplicationModule> extraIncludes;
private final BootstrapMode bootstrapMode;
private final ApplicationModule module;
private final ApplicationModules modules;
private final List<ApplicationModule> extraIncludes;
private final Supplier<List<JavaPackage>> basePackages;
private final Supplier<List<ApplicationModule>> dependencies;
private ModuleTestExecution(ApplicationModuleTest annotation, ApplicationModules modules, ApplicationModule module) {
this.key = Key.of(module.getBasePackage().getName(), annotation);
this.key = new Key(module.getBasePackage().getName(), annotation);
this.modules = modules;
this.bootstrapMode = annotation.mode();
this.module = module;
@@ -71,18 +67,18 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
this.basePackages = Suppliers.memoize(() -> {
Stream<JavaPackage> moduleBasePackages = module.getBootstrapBasePackages(modules, bootstrapMode.getDepth());
Stream<JavaPackage> sharedBasePackages = modules.getSharedModules().stream().map(it -> it.getBasePackage());
Stream<JavaPackage> extraPackages = extraIncludes.stream().map(ApplicationModule::getBasePackage);
var moduleBasePackages = module.getBootstrapBasePackages(modules, bootstrapMode.getDepth());
var sharedBasePackages = modules.getSharedModules().stream().map(it -> it.getBasePackage());
var extraPackages = extraIncludes.stream().map(ApplicationModule::getBasePackage);
Stream<JavaPackage> intermediate = Stream.concat(moduleBasePackages, extraPackages);
var intermediate = Stream.concat(moduleBasePackages, extraPackages);
return Stream.concat(intermediate, sharedBasePackages).distinct().toList();
});
this.dependencies = Suppliers.memoize(() -> {
Stream<ApplicationModule> bootstrapDependencies = module.getBootstrapDependencies(modules,
var bootstrapDependencies = module.getBootstrapDependencies(modules,
bootstrapMode.getDepth());
return Stream.concat(bootstrapDependencies, extraIncludes.stream()).toList();
});
@@ -96,17 +92,16 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
return () -> {
ApplicationModuleTest annotation = AnnotatedElementUtils.findMergedAnnotation(type, ApplicationModuleTest.class);
String packageName = type.getPackage().getName();
var annotation = AnnotatedElementUtils.findMergedAnnotation(type, ApplicationModuleTest.class);
var packageName = type.getPackage().getName();
Class<?> modulithType = MODULITH_TYPES.computeIfAbsent(type,
var modulithType = MODULITH_TYPES.computeIfAbsent(type,
it -> new AnnotatedClassFinder(SpringBootApplication.class).findFromPackage(packageName));
ApplicationModules modules = ApplicationModules.of(modulithType);
ApplicationModule module = modules.getModuleForPackage(packageName) //
.orElseThrow(
() -> new IllegalStateException(String.format("Package %s is not part of any module!", packageName)));
var modules = ApplicationModules.of(modulithType);
var module = modules.getModuleForPackage(packageName).orElseThrow( //
() -> new IllegalStateException(String.format("Package %s is not part of any module!", packageName)));
return EXECUTIONS.computeIfAbsent(Key.of(module.getBasePackage().getName(), annotation),
return EXECUTIONS.computeIfAbsent(new Key(module.getBasePackage().getName(), annotation),
it -> new ModuleTestExecution(annotation, modules, module));
};
}
@@ -122,11 +117,11 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
public boolean includes(String className) {
boolean result = modules.withinRootPackages(className) //
var result = modules.withinRootPackages(className) //
|| basePackages.get().stream().anyMatch(it -> it.contains(className));
if (result) {
LOG.trace("Including class {}.", className);
LOGGER.trace("Including class {}.", className);
}
return !result;
@@ -155,6 +150,42 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
module.verifyDependencies(modules);
}
/**
* Returns the {@link BootstrapMode} to be used for the executions.
*
* @return will never be {@literal null}.
*/
public BootstrapMode getBootstrapMode() {
return bootstrapMode;
}
/**
* Returns the primary {@link ApplicationModule} to bootstrap.
*
* @return the module will never be {@literal null}.
*/
public ApplicationModule getModule() {
return module;
}
/**
* Returns all {@link ApplicationModules} of the application.
*
* @return the modules will never be {@literal null}.
*/
public ApplicationModules getModules() {
return modules;
}
/**
* Returns all {@link ApplicationModule}s registered as extra includes for the execution.
*
* @return the extraIncludes will never be {@literal null}.
*/
public List<ApplicationModule> getExtraIncludes() {
return extraIncludes;
}
/*
* (non-Javadoc)
* @see java.lang.Iterable#iterator()
@@ -164,6 +195,33 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
return modules.iterator();
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ModuleTestExecution that)) {
return false;
}
return Objects.equals(key, that.key);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hash(key);
}
private static Stream<ApplicationModule> getExtraModules(ApplicationModuleTest annotation,
ApplicationModules modules) {
@@ -172,11 +230,5 @@ public class ModuleTestExecution implements Iterable<ApplicationModule> {
.flatMap(it -> it.map(Stream::of).orElseGet(Stream::empty));
}
@Value
@RequiredArgsConstructor(staticName = "of", access = AccessLevel.PRIVATE)
private static class Key {
String moduleBasePackage;
ApplicationModuleTest annotation;
}
private static record Key(String moduleBasePackage, ApplicationModuleTest annotation) {}
}

View File

@@ -15,9 +15,8 @@
*/
package org.springframework.modulith.test;
import lombok.EqualsAndHashCode;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Supplier;
import org.springframework.boot.context.TypeExcludeFilter;
@@ -27,7 +26,6 @@ import org.springframework.core.type.classreading.MetadataReaderFactory;
/**
* @author Oliver Drotbohm
*/
@EqualsAndHashCode(callSuper = false)
class ModuleTypeExcludeFilter extends TypeExcludeFilter {
private final Supplier<ModuleTestExecution> execution;
@@ -36,7 +34,7 @@ class ModuleTypeExcludeFilter extends TypeExcludeFilter {
this.execution = ModuleTestExecution.of(testClass);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.boot.context.TypeExcludeFilter#match(org.springframework.core.type.classreading.MetadataReader, org.springframework.core.type.classreading.MetadataReaderFactory)
*/
@@ -44,4 +42,31 @@ class ModuleTypeExcludeFilter extends TypeExcludeFilter {
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return execution.get().includes(metadataReader.getClassMetadata().getClassName());
}
/*
* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ModuleTypeExcludeFilter that)) {
return false;
}
return Objects.equals(execution, that.execution);
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hash(execution);
}
}

View File

@@ -17,9 +17,6 @@ package org.springframework.modulith.test;
import static org.assertj.core.api.Assertions.*;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -69,11 +66,22 @@ public class PublishedEventsAssert extends AbstractAssert<PublishedEventsAssert,
*
* @author Oliver Drotbohm
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public class PublishedEventAssert<T> {
private final TypedPublishedEvents<T> events;
/**
* Creates a new {@link PublishedEventAssert} for the given {@link TypedPublishedEvents}.
*
* @param events must not be {@literal null}.
*/
private PublishedEventAssert(TypedPublishedEvents<T> events) {
Assert.notNull(events, "TypedPublishedEvents must not be null!");
this.events = events;
}
/**
* Asserts that at least one event matches the given predicate.
*