diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleSource.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleSource.java index 0b56ef9b..8d8aefc3 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleSource.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleSource.java @@ -199,7 +199,7 @@ public class ApplicationModuleSource { @Override public Optional lookupIdentifier(JavaPackage pkg) { - return pkg.getAnnotation(annotation) + return pkg.findAnnotation(annotation) .map(extractor) .filter(StringUtils::hasText) .map(ApplicationModuleIdentifier::of); diff --git a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java index 0517d213..412ddd80 100644 --- a/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java +++ b/spring-modulith-core/src/main/java/org/springframework/modulith/core/JavaPackage.java @@ -253,8 +253,9 @@ public class JavaPackage implements DescribedIterable, Comparable Optional getAnnotation(Class annotationType) { - return packageClasses.that(have(simpleName(PACKAGE_INFO_NAME)) // - .and(are(metaAnnotatedWith(annotationType)))) // + var isPackageInfo = have(simpleName(PACKAGE_INFO_NAME)).or(are(metaAnnotatedWith(PackageInfo.class))); + + return packageClasses.that(isPackageInfo.and(are(metaAnnotatedWith(annotationType)))) // .toOptional() // .map(it -> it.reflect()) .map(it -> AnnotatedElementUtils.getMergedAnnotation(it, annotationType)); @@ -366,23 +367,22 @@ public class JavaPackage implements DescribedIterable, Comparable Optional findAnnotation(Class annotationType) { - return getAnnotation(annotationType) - .or(() -> { + var isPackageInfo = have(simpleName(PACKAGE_INFO_NAME)).or(are(metaAnnotatedWith(PackageInfo.class))); - var annotatedTypes = toSingle().packageClasses - .that(are(metaAnnotatedWith(PackageInfo.class).and(are(metaAnnotatedWith(annotationType))))) - .stream() - .map(it -> it.getAnnotationOfType(annotationType)) - .toList(); + var annotatedTypes = toSingle().packageClasses + .that(isPackageInfo.and(are(metaAnnotatedWith(annotationType)))) + .stream() + .map(JavaClass::reflect) + .map(it -> AnnotatedElementUtils.findMergedAnnotation(it, annotationType)) + .toList(); - if (annotatedTypes.size() > 1) { + if (annotatedTypes.size() > 1) { - throw new IllegalStateException(MULTIPLE_TYPES_ANNOTATED_WITH.formatted(name, - FormattableType.of(annotationType).getAbbreviatedFullName(), annotatedTypes)); - } + throw new IllegalStateException(MULTIPLE_TYPES_ANNOTATED_WITH.formatted(name, + FormattableType.of(annotationType).getAbbreviatedFullName(), annotatedTypes)); + } - return annotatedTypes.isEmpty() ? Optional.empty() : Optional.of(annotatedTypes.get(0)); - }); + return annotatedTypes.isEmpty() ? Optional.empty() : Optional.of(annotatedTypes.get(0)); } /** diff --git a/spring-modulith-core/src/test/java/org/springframework/modulith/core/ApplicationModuleSourceUnitTests.java b/spring-modulith-core/src/test/java/org/springframework/modulith/core/ApplicationModuleSourceUnitTests.java index b14c6772..39c6b287 100644 --- a/spring-modulith-core/src/test/java/org/springframework/modulith/core/ApplicationModuleSourceUnitTests.java +++ b/spring-modulith-core/src/test/java/org/springframework/modulith/core/ApplicationModuleSourceUnitTests.java @@ -56,4 +56,17 @@ class ApplicationModuleSourceUnitTests { .contains("module", // picked from package name not from annotation in nested package "gh-1042-nested"); // from annotation in nested package } + + @Test // GH-1052 + void detectsApplicationModuleMetadataOnAnnotatedType() { + + var pkg = TestUtils.getPackage("reproducers"); + + var sources = ApplicationModuleSource.from(pkg, root -> root.getSubPackage("gh1052").stream(), false); + + assertThat(sources).hasSize(1) + .extracting(ApplicationModuleSource::getIdentifier) + .extracting(ApplicationModuleIdentifier::toString) + .containsExactly("on-marker-type"); + } } diff --git a/spring-modulith-core/src/test/java/reproducers/gh1052/Marker.java b/spring-modulith-core/src/test/java/reproducers/gh1052/Marker.java new file mode 100644 index 00000000..abf69125 --- /dev/null +++ b/spring-modulith-core/src/test/java/reproducers/gh1052/Marker.java @@ -0,0 +1,24 @@ +/* + * 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 reproducers.gh1052; + +import org.springframework.modulith.ApplicationModule; + +/** + * @author Oliver Drotbohm + */ +@ApplicationModule(id = "on-marker-type") +class Marker {} diff --git a/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/JavaPackageUnitTests.java b/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/JavaPackageUnitTests.java index f23c9643..e1738e52 100644 --- a/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/JavaPackageUnitTests.java +++ b/spring-modulith-integration-test/src/test/java/org/springframework/modulith/core/JavaPackageUnitTests.java @@ -53,7 +53,7 @@ class JavaPackageUnitTests { void findsAnnotationOnPackageInfo() { var annotation = JavaPackage.of(Classes.of(PKG_CLASSES), "pkg.onpackage") // - .getAnnotation(ApplicationModule.class); + .findAnnotation(ApplicationModule.class); assertThat(annotation).hasValueSatisfying(it -> { assertThat(it.displayName()).isEqualTo("onPackage");