Skip to content

Commit 3af2506

Browse files
committed
GH-872 - Introduce proper identifiers for ApplicationModules.
Introduced ApplicationModuleIdentifier that ensures that identifiers chosen for application modules do not contain a double colon (::) as we need those as separator characters for manual dependency declarations referring to named interfaces.
1 parent 5733a27 commit 3af2506

File tree

10 files changed

+194
-43
lines changed

10 files changed

+194
-43
lines changed

spring-modulith-core/src/main/java/org/springframework/modulith/core/AnnotationModulithMetadata.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,15 @@ public Stream<String> getSharedModuleNames() {
115115
return Arrays.stream(annotation.sharedModules());
116116
}
117117

118+
/*
119+
* (non-Javadoc)
120+
* @see org.springframework.modulith.core.ModulithMetadata#getSharedModuleIdentifiers()
121+
*/
122+
@Override
123+
public Stream<ApplicationModuleIdentifier> getSharedModuleIdentifiers() {
124+
return getSharedModuleNames().map(ApplicationModuleIdentifier::of);
125+
}
126+
118127
/*
119128
* (non-Javadoc)
120129
* @see org.springframework.modulith.model.ModulithMetadata#getSystemName()

spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModule.java

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,21 @@ public NamedInterfaces getNamedInterfaces() {
143143
* Returns the logical name of the module.
144144
*
145145
* @return will never be {@literal null} or empty.
146+
* @deprecated since 1.3, use {@link #getIdentifier()} instead.
146147
*/
148+
@Deprecated
147149
public String getName() {
148-
return source.getModuleName();
150+
return getIdentifier().toString();
151+
}
152+
153+
/**
154+
* Returns the logical identifier of the module.
155+
*
156+
* @return will never be {@literal null}.
157+
* @since 1.3
158+
*/
159+
public ApplicationModuleIdentifier getIdentifier() {
160+
return source.getIdentifier();
149161
}
150162

151163
/**
@@ -306,7 +318,7 @@ public ArchitecturallyEvidentType getArchitecturallyEvidentType(Class<?> type) {
306318
return getType(type.getName())
307319
.map(it -> ArchitecturallyEvidentType.of(it, getSpringBeansInternal()))
308320
.orElseThrow(() -> new IllegalArgumentException("Couldn't find type %s in module %s!".formatted(
309-
FormattableType.of(type).getAbbreviatedFullName(this), getName())));
321+
FormattableType.of(type).getAbbreviatedFullName(this), getIdentifier())));
310322
}
311323

312324
/**
@@ -404,11 +416,11 @@ public String toString(@Nullable ApplicationModules modules) {
404416

405417
if (modules != null) {
406418
modules.getParentOf(this).ifPresent(it -> {
407-
builder.append("> Parent module: ").append(it.getName()).append("\n");
419+
builder.append("> Parent module: ").append(it.getIdentifier()).append("\n");
408420
});
409421
}
410422

411-
builder.append("> Logical name: ").append(getName()).append('\n');
423+
builder.append("> Logical name: ").append(getIdentifier()).append('\n');
412424
builder.append("> Base package: ").append(basePackage.getName()).append('\n');
413425

414426
builder.append("> Excluded packages: ");
@@ -438,8 +450,12 @@ public String toString(@Nullable ApplicationModules modules) {
438450
List<ApplicationModule> dependencies = getBootstrapDependencies(modules).toList();
439451

440452
builder.append("> Direct module dependencies: ");
441-
builder.append(dependencies.isEmpty() ? "none"
442-
: dependencies.stream().map(ApplicationModule::getName).collect(Collectors.joining(", ")));
453+
builder.append(dependencies.isEmpty()
454+
? "none"
455+
: dependencies.stream()
456+
.map(ApplicationModule::getIdentifier)
457+
.map(ApplicationModuleIdentifier::toString)
458+
.collect(Collectors.joining(", ")));
443459
builder.append('\n');
444460
}
445461

@@ -739,7 +755,7 @@ Classes getClasses() {
739755
}
740756

741757
private String getQualifiedName(NamedInterface namedInterface) {
742-
return namedInterface.getQualifiedName(getName());
758+
return namedInterface.getQualifiedName(getIdentifier());
743759
}
744760

745761
private Collection<ApplicationModule> doGetNestedModules(ApplicationModules modules, boolean recursive) {
@@ -877,7 +893,7 @@ public static DeclaredDependency of(String identifier, ApplicationModule source,
877893

878894
var target = modules.getModuleByName(targetModuleName)
879895
.orElseThrow(() -> new IllegalArgumentException(
880-
INVALID_EXPLICIT_MODULE_DEPENDENCY.formatted(source.getName(), targetModuleName)));
896+
INVALID_EXPLICIT_MODULE_DEPENDENCY.formatted(source.getIdentifier(), targetModuleName)));
881897

882898
if (WILDCARD.equals(namedInterfaceName)) {
883899
return new DeclaredDependency(target, null);
@@ -888,7 +904,8 @@ public static DeclaredDependency of(String identifier, ApplicationModule source,
888904
? namedInterfaces.getUnnamedInterface()
889905
: namedInterfaces.getByName(namedInterfaceName)
890906
.orElseThrow(() -> new IllegalArgumentException(
891-
INVALID_NAMED_INTERFACE_DECLARATION.formatted(namedInterfaceName, source.getName(), identifier)));
907+
INVALID_NAMED_INTERFACE_DECLARATION.formatted(namedInterfaceName, source.getIdentifier(),
908+
identifier)));
892909

893910
return new DeclaredDependency(target, namedInterface);
894911
}
@@ -943,7 +960,7 @@ boolean contains(Class<?> type) {
943960
@Override
944961
public String toString() {
945962

946-
var result = target.getName();
963+
var result = target.getIdentifier().toString();
947964

948965
if (namedInterface == null) {
949966
return result + " :: " + WILDCARD;
@@ -1230,14 +1247,14 @@ Violations isValidDependencyWithin(ApplicationModules modules) {
12301247
.toList();
12311248

12321249
var targetString = targetNamedInterfaces.isEmpty()
1233-
? "module '%s'".formatted(targetModule.getName())
1250+
? "module '%s'".formatted(targetModule.getIdentifier())
12341251
: "named interface(s) '%s'".formatted(
12351252
targetNamedInterfaces.stream()
12361253
.map(targetModule::getQualifiedName)
12371254
.collect(Collectors.joining(", ")));
12381255

12391256
var message = "Module '%s' depends on %s via %s -> %s. Allowed targets: %s." //
1240-
.formatted(originModule.getName(), targetString, source.getName(), target.getName(),
1257+
.formatted(originModule.getIdentifier(), targetString, source.getName(), target.getName(),
12411258
declaredDependencies.toString());
12421259

12431260
return violations.and(new Violation(message));
@@ -1256,7 +1273,7 @@ Violations isValidDependencyWithin(ApplicationModules modules) {
12561273
if (!targetModule.isExposed(target)) {
12571274

12581275
var violationText = INTERNAL_REFERENCE
1259-
.formatted(originModule.getName(), target.getName(), targetModule.getName());
1276+
.formatted(originModule.getIdentifier(), target.getName(), targetModule.getIdentifier());
12601277

12611278
return violations.and(new Violation(violationText + lineSeparator() + description));
12621279
}
@@ -1266,7 +1283,7 @@ Violations isValidDependencyWithin(ApplicationModules modules) {
12661283
if (!haveSameParentOrDirectParentRelationship(originModule, targetModule, modules)) {
12671284

12681285
var violationText = INVALID_SUB_MODULE_REFERENCE
1269-
.formatted(originModule.getName(), targetModule.getName(),
1286+
.formatted(originModule.getIdentifier(), targetModule.getIdentifier(),
12701287
FormattableType.of(source).getAbbreviatedFullName(originModule),
12711288
FormattableType.of(target).getAbbreviatedFullName(targetModule));
12721289

spring-modulith-core/src/main/java/org/springframework/modulith/core/ApplicationModuleDependencies.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ public boolean containsModuleNamed(String name) {
8585
Assert.hasText(name, "Module name must not be null or empty!");
8686

8787
return modules.stream()
88-
.map(ApplicationModule::getName)
88+
.map(ApplicationModule::getIdentifier)
89+
.map(Object::toString)
8990
.anyMatch(name::equals);
9091
}
9192

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.modulith.core;
17+
18+
import java.util.Objects;
19+
20+
import org.springframework.util.Assert;
21+
22+
/**
23+
* An identifier of an {@link ApplicationModule}.
24+
*
25+
* @author Oliver Drotbohm
26+
* @since 1.3
27+
*/
28+
public class ApplicationModuleIdentifier implements Comparable<ApplicationModuleIdentifier> {
29+
30+
private final String identifier;
31+
32+
/**
33+
* Creates a new {@link ApplicationModuleIdentifier} for the given {@link String}.
34+
*
35+
* @param identifier must not be {@literal null} .
36+
*/
37+
private ApplicationModuleIdentifier(String identifier) {
38+
39+
Assert.hasText(identifier, "Identitifier must not be null or empty!");
40+
Assert.isTrue(!identifier.contains("::"), "Identifier must not contain a double-colon!");
41+
42+
this.identifier = identifier;
43+
}
44+
45+
/**
46+
* Returns the {@link ApplicationModuleIdentifier} for the given source {@link String}.
47+
*
48+
* @param identifier must not be {@literal null}, empty or contain a double colon ({@code ::}).
49+
* @return will never be {@literal null}.
50+
*/
51+
public static ApplicationModuleIdentifier of(String identifier) {
52+
return new ApplicationModuleIdentifier(identifier);
53+
}
54+
55+
/*
56+
* (non-Javadoc)
57+
* @see java.lang.Comparable#compareTo(java.lang.Object)
58+
*/
59+
@Override
60+
public int compareTo(ApplicationModuleIdentifier o) {
61+
return identifier.compareTo(o.identifier);
62+
}
63+
64+
/*
65+
* (non-Javadoc)
66+
* @see java.lang.Object#toString()
67+
*/
68+
@Override
69+
public String toString() {
70+
return identifier;
71+
}
72+
73+
/*
74+
* (non-Javadoc)
75+
* @see java.lang.Object#equals(java.lang.Object)
76+
*/
77+
@Override
78+
public boolean equals(Object obj) {
79+
80+
if (obj == this) {
81+
return true;
82+
}
83+
84+
if (!(obj instanceof ApplicationModuleIdentifier that)) {
85+
return false;
86+
}
87+
88+
return Objects.equals(this.identifier, that.identifier);
89+
}
90+
91+
/*
92+
* (non-Javadoc)
93+
* @see java.lang.Object#hashCode()
94+
*/
95+
@Override
96+
public int hashCode() {
97+
return Objects.hash(identifier);
98+
}
99+
}

0 commit comments

Comments
 (0)