GH-267 - Explicitly declared empty allowed dependencies now forbids any dependency.
The default for @ApplicationModule(allowedDependencies) is now a single element list with a dedicated token we recognize as "all dependencies allowed". This allows users to declare an empty array explicitly to disallow any outgoing dependencies for an application module. Previously, such a declaration would have allowed any dependency.
This commit is contained in:
@@ -374,20 +374,20 @@ public class ApplicationModule {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all allowed module dependencies, either explicitly declared or defined as shared on the given
|
||||
* Returns all declared module dependencies, either explicitly declared or defined as shared on the given
|
||||
* {@link ApplicationModules} instance.
|
||||
*
|
||||
* @param modules must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
DeclaredDependencies getAllowedDependencies(ApplicationModules modules) {
|
||||
DeclaredDependencies getDeclaredDependencies(ApplicationModules modules) {
|
||||
|
||||
Assert.notNull(modules, "Modules must not be null!");
|
||||
|
||||
var allowedDependencyNames = information.getAllowedDependencies();
|
||||
var allowedDependencyNames = information.getDeclaredDependencies();
|
||||
|
||||
if (allowedDependencyNames.isEmpty()) {
|
||||
return new DeclaredDependencies(Collections.emptyList());
|
||||
if (DeclaredDependencies.isOpen(allowedDependencyNames)) {
|
||||
return DeclaredDependencies.open();
|
||||
}
|
||||
|
||||
var explicitlyDeclaredModules = allowedDependencyNames.stream() //
|
||||
@@ -398,7 +398,7 @@ public class ApplicationModule {
|
||||
|
||||
return Stream.concat(explicitlyDeclaredModules, sharedDependencies) //
|
||||
.distinct() //
|
||||
.collect(Collectors.collectingAndThen(Collectors.toList(), DeclaredDependencies::new));
|
||||
.collect(Collectors.collectingAndThen(Collectors.toList(), DeclaredDependencies::closed));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -655,6 +655,19 @@ public class ApplicationModule {
|
||||
return namedInterface.contains(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link DeclaredDependency} contains the given {@link Class}.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
|
||||
return namedInterface.contains(type);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
@@ -701,38 +714,55 @@ public class ApplicationModule {
|
||||
*/
|
||||
static class DeclaredDependencies {
|
||||
|
||||
private static final String OPEN_TOKEN = "¯\\_(ツ)_/¯";
|
||||
|
||||
private final List<DeclaredDependency> dependencies;
|
||||
private final boolean closed;
|
||||
|
||||
static boolean isOpen(List<String> declaredDependencies) {
|
||||
return declaredDependencies.size() == 1 && declaredDependencies.get(0).equals(OPEN_TOKEN);
|
||||
}
|
||||
|
||||
public static DeclaredDependencies open() {
|
||||
return new DeclaredDependencies(Collections.emptyList(), false);
|
||||
}
|
||||
|
||||
public static DeclaredDependencies closed(List<DeclaredDependency> dependencies) {
|
||||
return new DeclaredDependencies(dependencies, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link DeclaredDependencies} for the given {@link List} of {@link DeclaredDependency}.
|
||||
*
|
||||
* @param dependencies must not be {@literal null}.
|
||||
*/
|
||||
public DeclaredDependencies(List<DeclaredDependency> dependencies) {
|
||||
private DeclaredDependencies(List<DeclaredDependency> dependencies, boolean closed) {
|
||||
|
||||
Assert.notNull(dependencies, "Dependencies must not be null!");
|
||||
|
||||
this.dependencies = dependencies;
|
||||
this.closed = closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether any of the dependencies contains the given {@link JavaClass}.
|
||||
* Returns whether the given {@link JavaClass} is a valid dependency.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public boolean contains(JavaClass type) {
|
||||
|
||||
Assert.notNull(type, "JavaClass must not be null!");
|
||||
|
||||
return dependencies.stream() //
|
||||
.anyMatch(it -> it.contains(type));
|
||||
public boolean isAllowedDependency(JavaClass type) {
|
||||
return isAllowedDependency(it -> it.contains(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link DeclaredDependencies} are empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return dependencies.isEmpty();
|
||||
public boolean isAllowedDependency(Class<?> type) {
|
||||
return isAllowedDependency(it -> it.contains(type));
|
||||
}
|
||||
|
||||
private boolean isAllowedDependency(Predicate<DeclaredDependency> predicate) {
|
||||
|
||||
Assert.notNull(predicate, "Predicate must not be null!");
|
||||
|
||||
return closed ? !dependencies.isEmpty() && contains(predicate) : dependencies.isEmpty() || contains(predicate);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -742,9 +772,9 @@ public class ApplicationModule {
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
return dependencies.stream() //
|
||||
.map(DeclaredDependency::toString)
|
||||
.collect(Collectors.joining(", "));
|
||||
return dependencies.isEmpty() //
|
||||
? "none" //
|
||||
: dependencies.stream().map(DeclaredDependency::toString).collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -773,6 +803,18 @@ public class ApplicationModule {
|
||||
public int hashCode() {
|
||||
return Objects.hash(dependencies);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether any of the dependencies contains the given {@link JavaClass}.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
*/
|
||||
private boolean contains(Predicate<DeclaredDependency> condition) {
|
||||
|
||||
Assert.notNull(condition, "Condition must not be null!");
|
||||
|
||||
return dependencies.stream().anyMatch(condition);
|
||||
}
|
||||
}
|
||||
|
||||
static class QualifiedDependency {
|
||||
@@ -880,16 +922,15 @@ public class ApplicationModule {
|
||||
var originModule = getExistingModuleOf(source, modules);
|
||||
var targetModule = getExistingModuleOf(target, modules);
|
||||
|
||||
DeclaredDependencies allowedTargets = originModule.getAllowedDependencies(modules);
|
||||
DeclaredDependencies declaredDependencies = originModule.getDeclaredDependencies(modules);
|
||||
Violations violations = Violations.NONE;
|
||||
|
||||
// Check explicitly defined allowed targets
|
||||
|
||||
if (!allowedTargets.isEmpty() && !allowedTargets.contains(target)) {
|
||||
if (!declaredDependencies.isAllowedDependency(target)) {
|
||||
|
||||
var message = "Module '%s' depends on module '%s' via %s -> %s. Allowed targets: %s." //
|
||||
.formatted(originModule.getName(), targetModule.getName(), source.getName(), target.getName(),
|
||||
allowedTargets.toString());
|
||||
declaredDependencies.toString());
|
||||
|
||||
return violations.and(new IllegalStateException(message));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springframework.modulith.core;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
@@ -67,7 +66,7 @@ interface ApplicationModuleInformation {
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
List<String> getAllowedDependencies();
|
||||
List<String> getDeclaredDependencies();
|
||||
|
||||
/**
|
||||
* An {@link ApplicationModuleInformation} for the jMolecules {@link Module} annotation.
|
||||
@@ -106,11 +105,11 @@ interface ApplicationModuleInformation {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.modulith.model.ApplicationModuleInformation#getAllowedDependencies()
|
||||
* @see org.springframework.modulith.core.ApplicationModuleInformation#getDeclaredDependencies()
|
||||
*/
|
||||
@Override
|
||||
public List<String> getAllowedDependencies() {
|
||||
return Collections.emptyList();
|
||||
public List<String> getDeclaredDependencies() {
|
||||
return List.of(ApplicationModule.OPEN_TOKEN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,14 +157,14 @@ interface ApplicationModuleInformation {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.modulith.model.ApplicationModuleInformation#getAllowedDependencies()
|
||||
* @see org.springframework.modulith.core.ApplicationModuleInformation#getDeclaredDependencies()
|
||||
*/
|
||||
@Override
|
||||
public List<String> getAllowedDependencies() {
|
||||
public List<String> getDeclaredDependencies() {
|
||||
|
||||
return annotation //
|
||||
.map(it -> Arrays.stream(it.allowedDependencies())) //
|
||||
.orElse(Stream.empty()) //
|
||||
.orElse(Stream.of(ApplicationModule.OPEN_TOKEN)) //
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user