Modulith support (continued)

This commit is contained in:
aboyko
2023-07-04 19:43:43 -04:00
parent 31a8600fee
commit 3d8d2e58f9
5 changed files with 73 additions and 18 deletions

View File

@@ -10,6 +10,7 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@@ -17,6 +18,7 @@ import org.openrewrite.ExecutionContext;
import org.openrewrite.Tree;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.marker.JavaSourceSet;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.openrewrite.java.tree.J.FieldAccess;
@@ -33,6 +35,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemTy
import org.springframework.ide.vscode.commons.rewrite.config.MarkerVisitorContext;
import org.springframework.ide.vscode.commons.rewrite.config.RecipeCodeActionDescriptor;
import org.springframework.ide.vscode.commons.rewrite.java.FixAssistMarker;
import org.springframework.ide.vscode.commons.rewrite.java.ProjectParser;
public class ModulithTypeReferenceViolation implements RecipeCodeActionDescriptor {
@@ -59,6 +62,10 @@ public class ModulithTypeReferenceViolation implements RecipeCodeActionDescripto
if (appModules == null) {
return cu;
} else {
JavaSourceSet sourceSet = cu.getMarkers().findFirst(JavaSourceSet.class).orElse(null);
if (sourceSet != null && ProjectParser.TEST.equals(sourceSet.getName())) {
return cu;
}
String pkgName = cu.getPackageDeclaration() == null ? "" : cu.getPackageDeclaration().getPackageName();
p.putMessage(MSG_PKG_NAME, pkgName);
return super.visitCompilationUnit(cu, p);
@@ -76,19 +83,26 @@ public class ModulithTypeReferenceViolation implements RecipeCodeActionDescripto
@Override
public Identifier visitIdentifier(Identifier identifier, ExecutionContext p) {
Identifier i = super.visitIdentifier(identifier, p);
if (!(getCursor().getParent().firstEnclosingOrThrow(J.class) instanceof J.FieldAccess)) {
return process(i, p.getMessage(MSG_PKG_NAME), TypeUtils.asFullyQualified(identifier.getType()));
if (getCursor().getParent().firstEnclosingOrThrow(J.class) instanceof J.FieldAccess) {
return i;
}
// check if identifier is a simple name of the type
FullyQualified type = TypeUtils.asFullyQualified(identifier.getType());
if (type != null && identifier.getSimpleName().equals(type.getClassName())) {
return process(i, p.getMessage(MSG_PKG_NAME), type);
}
return i;
}
private <T extends J> T process(T node, String packageName, FullyQualified type) {
if (type != null) {
if (!appModules.isReferenceAllowed(packageName, type.getFullyQualifiedName())) {
Optional<T> opt = appModules.getModuleNotExposingType(packageName, type.getFullyQualifiedName()).map(module -> {
FixAssistMarker fixMarker = new FixAssistMarker(Tree.randomId(), getId())
.withLabel("Type is not allowed to be used in this package. Consider changing you 'Modulith' structure." );
node = node.withMarkers(node.getMarkers().add(fixMarker));
}
.withLabel("Cannot use type in this package. Type is not exposed in module '" + module.name() + "'." );
return node.withMarkers(node.getMarkers().add(fixMarker));
});
return opt.orElse(node);
}
return node;
}

View File

@@ -31,8 +31,7 @@ public class ServerUtils {
private static final List<String> CLASS_FILES_TO_WATCH_GLOB = List.of("**/*.class");
public static void listenToClassFileChanges(FileObserver fileObserver, JavaProjectFinder projectFinder, Consumer<IJavaProject> callback) {
fileObserver.onFilesChanged(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
fileObserver.onFilesCreated(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
fileObserver.onAnyChange(CLASS_FILES_TO_WATCH_GLOB, files -> handleFiles(projectFinder, files, callback));
}
private static void handleFiles(JavaProjectFinder projectFinder, String[] files, Consumer<IJavaProject> callback) {

View File

@@ -12,7 +12,7 @@ package org.springframework.ide.vscode.boot.modulith;
import java.util.Collection;
record AppModule(
public record AppModule(
String name,
String basePackage,
Collection<String> namedInterfaces

View File

@@ -10,7 +10,9 @@
*******************************************************************************/
package org.springframework.ide.vscode.boot.modulith;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public final class AppModules {
@@ -20,14 +22,42 @@ public final class AppModules {
this.modules = modules;
}
public boolean isReferenceAllowed(String targetPackage, String referenceFqName) {
String referencePackage = ModulithService.getPackageNameFromTypeFQName(referenceFqName);
return modules
.stream()
.filter(m -> m.basePackage().equals(referencePackage))
.findFirst()
.map(m -> m.namedInterfaces().contains(referenceFqName))
.orElse(true);
public Optional<AppModule> getModuleNotExposingType(String targetPackage, String referenceFqName) {
return getModuleForType(referenceFqName).flatMap(refModule -> {
if (refModule.namedInterfaces().contains(referenceFqName)) {
return Optional.empty();
} else {
if (getModuleForPackage(targetPackage).map(targetModule -> targetModule == refModule).orElse(false)) {
// same module for target package and reference type
return Optional.empty();
}
return Optional.of(refModule);
}
});
}
private Optional<AppModule> getModuleForPackage(String pkgName) {
return generatePackageHierarchy(pkgName)
.stream()
.map(p -> modules.stream().filter(m -> m.basePackage().equals(p)).findFirst())
.filter(o -> o.isPresent())
.map(o -> o.get())
.findFirst();
}
private Optional<AppModule> getModuleForType(String typeFqName) {
return getModuleForPackage(ModulithService.getPackageNameFromTypeFQName(typeFqName));
}
private List<String> generatePackageHierarchy(String pkgName) {
List<String> packageHierarchy = new ArrayList<>();
packageHierarchy.add(pkgName);
for (int i = pkgName.length() - 1; i >= 0; i--) {
if (pkgName.charAt(i) == '.') {
packageHierarchy.add(pkgName.substring(0, i));
}
}
return packageHierarchy;
}
}

View File

@@ -13,6 +13,8 @@ package org.springframework.ide.vscode.boot.modulith;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -29,6 +31,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.commons.java.IClasspathUtil;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
@@ -85,7 +88,16 @@ public class ModulithService {
for (String f : files) {
URI uri = URI.create(f);
TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toASCIIString());
projectFinder.find(docId).ifPresent(this::invalidate);
projectFinder.find(docId).ifPresent(project -> {
synchronized (project) {
if (cache.containsKey(project)) {
Path filePath = Paths.get(uri);
if (IClasspathUtil.getProjectJavaSourceFoldersWithoutTests(project.getClasspath()).map(folder -> folder.toPath()).anyMatch(folderPath -> filePath.startsWith(folderPath))) {
cache.remove(project);
}
}
}
});
}
}