From 18f10b04019c3e89bc65e114756ada4fb20fb96b Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Sat, 25 Jan 2025 13:49:53 +0100 Subject: [PATCH] GH-1038 - Expose ApplicationModuleInformation. ApplicationModuleInformation is now public as it's referenced from the ApplicationModuleDetectionStrategy SPI. Extracted ApplicationModuleInformationFactory to avoid the internal instance creation logic from leaking. --- .../core/ApplicationModuleInformation.java | 160 +-------------- .../ApplicationModuleInformationFactory.java | 193 ++++++++++++++++++ 2 files changed, 196 insertions(+), 157 deletions(-) create mode 100644 spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformationFactory.java diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformation.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformation.java index 4e1d1e5e..800c8d18 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformation.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformation.java @@ -15,30 +15,17 @@ */ package org.springframework.modulith.core; -import java.lang.annotation.Annotation; -import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Stream; - -import org.jmolecules.ddd.annotation.Module; -import org.springframework.modulith.ApplicationModule; -import org.springframework.modulith.ApplicationModule.Type; -import org.springframework.modulith.core.Types.JMoleculesTypes; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -import com.tngtech.archunit.core.domain.JavaClass; /** * Abstraction for low-level module information. Used to support different annotations to configure metadata about a * module. * * @author Oliver Drotbohm + * @since 1.4, previously package private. */ -interface ApplicationModuleInformation { +public interface ApplicationModuleInformation { /** * Creates a new {@link ApplicationModuleInformation} for the given {@link JavaPackage}. @@ -47,12 +34,7 @@ interface ApplicationModuleInformation { * @return will never be {@literal null}. */ public static ApplicationModuleInformation of(JavaPackage javaPackage) { - - var lookup = AnnotationLookup.of(javaPackage.toSingle(), __ -> true); - - return JMoleculesTypes.isModulePresent() && JMoleculesModule.supports(lookup) - ? new JMoleculesModule(lookup) - : new SpringModulithModule(lookup); + return ApplicationModuleInformationFactory.of(javaPackage); } /** @@ -78,140 +60,4 @@ interface ApplicationModuleInformation { * @since 1.2 */ boolean isOpen(); - - /** - * An {@link ApplicationModuleInformation} for the jMolecules {@link Module} annotation. - * - * @author Oliver Drotbohm - * @see https://jMolecules.org - */ - static class JMoleculesModule implements ApplicationModuleInformation { - - private final Optional annotation; - - public static boolean supports(AnnotationLookup lookup) { - return lookup.lookup(Module.class).isPresent(); - } - - public JMoleculesModule(AnnotationLookup lookup) { - this.annotation = lookup.lookup(Module.class); - } - - /* - * (non-Javadoc) - * @see org.springframework.modulith.model.ApplicationModuleInformation#getDisplayName() - */ - @Override - public Optional getDisplayName() { - - Supplier> fallback = () -> annotation // - .map(Module::value) // - .filter(StringUtils::hasText); - - return annotation // - .map(Module::name) // - .filter(StringUtils::hasText) - .or(fallback); - } - - /* - * (non-Javadoc) - * @see org.springframework.modulith.core.ApplicationModuleInformation#getDeclaredDependencies() - */ - @Override - public List getDeclaredDependencies() { - return List.of(ApplicationModule.OPEN_TOKEN); - } - - /* - * (non-Javadoc) - * @see org.springframework.modulith.core.ApplicationModuleInformation#isOpenModule() - */ - @Override - public boolean isOpen() { - return false; - } - } - - /** - * An {@link ApplicationModuleInformation} that inspects the {@link ApplicationModule} annotation. - * - * @author Oliver Drotbohm - */ - static class SpringModulithModule implements ApplicationModuleInformation { - - private final Optional annotation; - - /** - * Whether the given {@link AnnotationLookup} supports this {@link ApplicationModuleInformation}. - * - * @param lookup must not be {@literal null}. - */ - public static boolean supports(AnnotationLookup lookup) { - - Assert.notNull(lookup, "Annotation lookup must not be null!"); - - return lookup.lookup(ApplicationModule.class).isPresent(); - } - - /** - * Creates a new {@link SpringModulithModule} for the given {@link AnnotationLookup}. - * - * @param lookup must not be {@literal null}. - */ - public SpringModulithModule(AnnotationLookup lookup) { - this.annotation = lookup.lookup(ApplicationModule.class); - } - - /* - * (non-Javadoc) - * @see org.springframework.modulith.model.ApplicationModuleInformation#getDisplayName() - */ - @Override - public Optional getDisplayName() { - - return annotation // - .map(ApplicationModule::displayName) // - .filter(StringUtils::hasText); - } - - /* - * (non-Javadoc) - * @see org.springframework.modulith.core.ApplicationModuleInformation#getDeclaredDependencies() - */ - @Override - public List getDeclaredDependencies() { - - return annotation // - .map(it -> Arrays.stream(it.allowedDependencies())) // - .orElse(Stream.of(ApplicationModule.OPEN_TOKEN)) // - .toList(); - } - - /* - * (non-Javadoc) - * @see org.springframework.modulith.core.ApplicationModuleInformation#isOpenModule() - */ - @Override - public boolean isOpen() { - return annotation.map(it -> it.type().equals(Type.OPEN)).orElse(false); - } - } - - interface AnnotationLookup { - - static AnnotationLookup of(JavaPackage javaPackage, - Predicate typeSelector) { - - return new AnnotationLookup() { - - @Override - public Optional lookup(Class annotation) { - return javaPackage.findAnnotation(annotation); - } - }; - } - - Optional lookup(Class annotation); - } } diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformationFactory.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformationFactory.java new file mode 100644 index 00000000..03d472f3 --- /dev/null +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleInformationFactory.java @@ -0,0 +1,193 @@ +/* + * Copyright 2025 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.modulith.core; + +import java.lang.annotation.Annotation; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.jmolecules.ddd.annotation.Module; +import org.springframework.modulith.ApplicationModule; +import org.springframework.modulith.ApplicationModule.Type; +import org.springframework.modulith.core.Types.JMoleculesTypes; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +import com.tngtech.archunit.core.domain.JavaClass; + +/** + * Encapsulate creation logic for {@link ApplicationModuleInformation} instances. + * + * @author Oliver Drotbohm + * @since 1.4 + */ +class ApplicationModuleInformationFactory { + + /** + * Creates a new {@link ApplicationModuleInformation} for the given {@link JavaPackage}. + * + * @param javaPackage must not be {@literal null}. + * @return will never be {@literal null}. + */ + public static ApplicationModuleInformation of(JavaPackage javaPackage) { + + var lookup = AnnotationLookup.of(javaPackage.toSingle(), __ -> true); + + return JMoleculesTypes.isModulePresent() && JMoleculesModule.supports(lookup) + ? new JMoleculesModule(lookup) + : new SpringModulithModule(lookup); + } + + /** + * An {@link ApplicationModuleInformation} for the jMolecules {@link Module} annotation. + * + * @author Oliver Drotbohm + * @see https://jMolecules.org + */ + static class JMoleculesModule implements ApplicationModuleInformation { + + private final Optional annotation; + + public static boolean supports(AnnotationLookup lookup) { + return lookup.lookup(Module.class).isPresent(); + } + + public JMoleculesModule(AnnotationLookup lookup) { + this.annotation = lookup.lookup(Module.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.model.ApplicationModuleInformation#getDisplayName() + */ + @Override + public Optional getDisplayName() { + + Supplier> fallback = () -> annotation // + .map(Module::value) // + .filter(StringUtils::hasText); + + return annotation // + .map(Module::name) // + .filter(StringUtils::hasText) + .or(fallback); + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.core.ApplicationModuleInformation#getDeclaredDependencies() + */ + @Override + public List getDeclaredDependencies() { + return List.of(ApplicationModule.OPEN_TOKEN); + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.core.ApplicationModuleInformation#isOpenModule() + */ + @Override + public boolean isOpen() { + return false; + } + } + + /** + * An {@link ApplicationModuleInformation} that inspects the {@link ApplicationModule} annotation. + * + * @author Oliver Drotbohm + */ + static class SpringModulithModule implements ApplicationModuleInformation { + + private final Optional annotation; + + /** + * Whether the given {@link AnnotationLookup} supports this {@link ApplicationModuleInformation}. + * + * @param lookup must not be {@literal null}. + */ + public static boolean supports(AnnotationLookup lookup) { + + Assert.notNull(lookup, "Annotation lookup must not be null!"); + + return lookup.lookup(ApplicationModule.class).isPresent(); + } + + /** + * Creates a new {@link SpringModulithModule} for the given {@link AnnotationLookup}. + * + * @param lookup must not be {@literal null}. + */ + public SpringModulithModule(AnnotationLookup lookup) { + this.annotation = lookup.lookup(ApplicationModule.class); + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.model.ApplicationModuleInformation#getDisplayName() + */ + @Override + public Optional getDisplayName() { + + return annotation // + .map(ApplicationModule::displayName) // + .filter(StringUtils::hasText); + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.core.ApplicationModuleInformation#getDeclaredDependencies() + */ + @Override + public List getDeclaredDependencies() { + + return annotation // + .map(it -> Arrays.stream(it.allowedDependencies())) // + .orElse(Stream.of(ApplicationModule.OPEN_TOKEN)) // + .toList(); + } + + /* + * (non-Javadoc) + * @see org.springframework.modulith.core.ApplicationModuleInformation#isOpenModule() + */ + @Override + public boolean isOpen() { + return annotation.map(it -> it.type().equals(Type.OPEN)).orElse(false); + } + } + + interface AnnotationLookup { + + static AnnotationLookup of(JavaPackage javaPackage, + Predicate typeSelector) { + + return new AnnotationLookup() { + + @Override + public Optional lookup(Class annotation) { + return javaPackage.findAnnotation(annotation); + } + }; + } + + Optional lookup(Class annotation); + } +}