Modulith support

This commit is contained in:
aboyko
2023-06-29 18:02:38 -04:00
parent cd8e75561b
commit 31a8600fee
17 changed files with 480 additions and 46 deletions

View File

@@ -25,8 +25,9 @@ public class Bean {
private final Location location;
private final InjectionPoint[] injectionPoints;
private final Set<String> supertypes;
private final String[] annotations;
public Bean(String name, String type, Location location, InjectionPoint[] injectionPoints, String[] supertypes) {
public Bean(String name, String type, Location location, InjectionPoint[] injectionPoints, String[] supertypes, String[] annotations) {
this.name = name;
this.type = type;
this.location = location;
@@ -39,6 +40,7 @@ public class Bean {
}
this.supertypes = new HashSet<>(Arrays.asList(supertypes));
this.annotations = annotations;
}
public String getName() {
@@ -61,6 +63,10 @@ public class Bean {
return type != null && ((this.type != null && this.type.equals(type)) || (supertypes.contains(type)));
}
public String[] getAnnotations() {
return annotations;
}
@Override
public String toString() {
Gson gson = new Gson();

View File

@@ -61,6 +61,11 @@
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.modulith</groupId>
<artifactId>spring-modulith-core</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>commons-yaml</artifactId>
@@ -192,6 +197,11 @@
<version>${dependencies.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.ide.vscode</groupId>
<artifactId>bosh-language-server</artifactId>
<version>1.48.0-SNAPSHOT</version>
</dependency>
</dependencies>
<profiles>

View File

@@ -40,8 +40,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.ide.vscode.boot.common.PropertyCompletionFactory;
import org.springframework.ide.vscode.boot.common.RelaxedNameConfig;
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
import org.springframework.ide.vscode.boot.java.JavaDefinitionHandler;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaCodeActionProvider;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaProjectReconcilerScheduler;
import org.springframework.ide.vscode.boot.java.handlers.BootJavaReconcileEngine;
@@ -74,6 +74,7 @@ import org.springframework.ide.vscode.boot.metadata.LoggerNameProvider;
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndex;
import org.springframework.ide.vscode.boot.metadata.ValueProviderRegistry;
import org.springframework.ide.vscode.boot.modulith.ModulithService;
import org.springframework.ide.vscode.boot.properties.completions.SpringPropertiesCompletionEngine;
import org.springframework.ide.vscode.boot.xml.SpringXMLCompletionEngine;
import org.springframework.ide.vscode.boot.yaml.completions.ApplicationYamlAssistContext;
@@ -374,4 +375,9 @@ public class BootLanguageServerBootApp {
return new JavaDefinitionHandler(cuCache, projectFinder, List.of(new PropertyValueAnnotationDefProvider()));
}
@Bean
ModulithService modulithService(SimpleLanguageServer server, ProjectObserver projectObserver, SpringMetamodelIndex springIndex, JavaProjectFinder projectFinder) {
return new ModulithService(projectObserver, server.getWorkspaceService().getFileObserver(), projectFinder, springIndex);
}
}

View File

@@ -19,6 +19,7 @@ public class Annotations {
public static final String BEAN = "org.springframework.context.annotation.Bean";
public static final String PROFILE = "org.springframework.context.annotation.Profile";
public static final String CONDITIONAL = "org.springframework.context.annotation.Conditional";
public static final String BOOT_APP = "org.springframework.boot.autoconfigure.SpringBootApplication";
public static final String COMPONENT = "org.springframework.stereotype.Component";
public static final String CONFIGURATION = "org.springframework.context.annotation.Configuration";

View File

@@ -25,7 +25,9 @@ public enum Boot3JavaProblemType implements ProblemType {
JAVA_TYPE_NOT_SUPPORTED(ERROR, "Type no supported as of Spring Boot 3", "Type not supported as of Spring Boot 3"),
FACTORIES_KEY_NOT_SUPPORTED(ERROR, "Spring factories key not supported", "Spring factories key not supported");
FACTORIES_KEY_NOT_SUPPORTED(ERROR, "Spring factories key not supported", "Spring factories key not supported"),
MODULITH_TYPE_REF_VIOLATION(ERROR, "Restricted dependency", "Import from restricted module");
private final ProblemSeverity defaultSeverity;
private String description;

View File

@@ -32,6 +32,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
@@ -93,8 +94,12 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
Set<String> supertypes = new HashSet<>();
ASTUtils.findSupertypes(beanType, supertypes);
String[] annotations = AnnotationHierarchies
.findTransitiveSuperAnnotationBindings(node.resolveAnnotationBinding())
.map(t -> t.getAnnotationType().getQualifiedName()).toArray(String[]::new);
Bean beanDefinition = new Bean(nameAndRegion.getT1(), beanType.getQualifiedName(), location, injectionPoints, (String[]) supertypes.toArray(new String[supertypes.size()]));
Bean beanDefinition = new Bean(nameAndRegion.getT1(), beanType.getQualifiedName(), location, injectionPoints, (String[]) supertypes.toArray(new String[supertypes.size()]), annotations);
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol, beanDefinition));
@@ -213,7 +218,7 @@ public class BeansSymbolProvider extends AbstractSymbolProvider {
}
return result.toString();
}
private boolean isMethodAbstract(MethodDeclaration method) {
List<?> modifiers = method.modifiers();
for (Object modifier : modifiers) {

View File

@@ -14,6 +14,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ITypeBinding;
@@ -27,6 +28,7 @@ import org.eclipse.lsp4j.jsonrpc.messages.Tuple.Two;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ide.vscode.boot.java.Annotations;
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
import org.springframework.ide.vscode.boot.java.handlers.AbstractSymbolProvider;
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
@@ -91,8 +93,10 @@ public class ComponentSymbolProvider extends AbstractSymbolProvider {
Set<String> supertypes = new HashSet<>();
ASTUtils.findSupertypes(beanType, supertypes);
String[] annotations = Stream.concat(Stream.of(annotationType), metaAnnotations.stream()).map(t -> t.getQualifiedName()).toArray(String[]::new);
Bean beanDefinition = new Bean(beanName, beanType.getQualifiedName(), location, injectionPoints, (String[]) supertypes.toArray(new String[supertypes.size()]));
Bean beanDefinition = new Bean(beanName, beanType.getQualifiedName(), location, injectionPoints, (String[]) supertypes.toArray(new String[supertypes.size()]), annotations);
return Tuple.two(new EnhancedSymbolInformation(symbol, addon), beanDefinition);
}

View File

@@ -72,7 +72,7 @@ public class DataRepositorySymbolProvider extends AbstractSymbolProvider {
ASTUtils.findSupertypes(concreteBeanTypeBindung, supertypes);
String concreteRepoType = concreteBeanTypeBindung.getQualifiedName();
Bean beanDefinition = new Bean(beanName, concreteRepoType, location, injectionPoints, (String[]) supertypes.toArray(new String[supertypes.size()]));
Bean beanDefinition = new Bean(beanName, concreteRepoType, location, injectionPoints, (String[]) supertypes.toArray(new String[supertypes.size()]), new String[0]);
context.getGeneratedSymbols().add(new CachedSymbol(context.getDocURI(), context.getLastModified(), enhancedSymbol, beanDefinition));

View File

@@ -20,6 +20,7 @@ import org.springframework.ide.vscode.boot.java.rewrite.reconcile.BeanPostProces
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.Boot3NotSupportedTypeProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.EntityIdForRepoProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.HttpSecurityLamdaDslCodeAction;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.ModulithTypeReferenceViolation;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoAutowiredOnConstructorProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoRepoAnnotationProblem;
import org.springframework.ide.vscode.boot.java.rewrite.reconcile.NoRequestMappingAnnotationCodeAction;
@@ -51,7 +52,8 @@ public class BootCodeActionRepository extends CodeActionRepository {
new AddConfigurationIfBeansPresentCodeAction(),
new AuthorizeHttpRequestsCodeAction(),
new WebSecurityConfigurerAdapterCodeAction(),
new EntityIdForRepoProblem()
new EntityIdForRepoProblem(),
new ModulithTypeReferenceViolation()
);
}

View File

@@ -0,0 +1,110 @@
/*******************************************************************************
* Copyright (c) 2023 VMware, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.java.rewrite.reconcile;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Tree;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.J.CompilationUnit;
import org.openrewrite.java.tree.J.FieldAccess;
import org.openrewrite.java.tree.J.Identifier;
import org.openrewrite.java.tree.JavaType.FullyQualified;
import org.openrewrite.java.tree.TypeUtils;
import org.springframework.ide.vscode.boot.java.Boot3JavaProblemType;
import org.springframework.ide.vscode.boot.modulith.AppModules;
import org.springframework.ide.vscode.boot.modulith.ModulithService;
import org.springframework.ide.vscode.commons.java.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
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;
public class ModulithTypeReferenceViolation implements RecipeCodeActionDescriptor {
private static final String MSG_PKG_NAME = "packageName";
@Override
public JavaVisitor<ExecutionContext> getMarkerVisitor(MarkerVisitorContext context) {
ModulithService modulithService = context.appContext().getBean(ModulithService.class);
CompletableFuture<AppModules> future = modulithService.getModulesData(context.project());
AppModules modules = null;
try {
modules = future == null ? null : future.get();
} catch (InterruptedException | ExecutionException e) {
// ignore
}
final AppModules appModules = modules;
return new JavaIsoVisitor<ExecutionContext>() {
@Override
public CompilationUnit visitCompilationUnit(CompilationUnit cu, ExecutionContext p) {
if (appModules == null) {
return cu;
} else {
String pkgName = cu.getPackageDeclaration() == null ? "" : cu.getPackageDeclaration().getPackageName();
p.putMessage(MSG_PKG_NAME, pkgName);
return super.visitCompilationUnit(cu, p);
}
}
@Override
public FieldAccess visitFieldAccess(FieldAccess fieldAccess, ExecutionContext p) {
FieldAccess fa = super.visitFieldAccess(fieldAccess, p);
return process(fa, p.getMessage(MSG_PKG_NAME), TypeUtils.asFullyQualified(fa.getType()));
}
@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()));
}
return i;
}
private <T extends J> T process(T node, String packageName, FullyQualified type) {
if (type != null) {
if (!appModules.isReferenceAllowed(packageName, type.getFullyQualifiedName())) {
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));
}
}
return node;
}
};
}
@Override
public boolean isApplicable(IJavaProject project) {
Version v = SpringProjectUtil.getDependencyVersion(project, "spring-modulith-core");
return v != null;
}
@Override
public ProblemType getProblemType() {
return Boot3JavaProblemType.MODULITH_TYPE_REF_VIOLATION;
}
}

View File

@@ -381,8 +381,11 @@ public class SymbolCacheOnDisc implements SymbolCache {
JsonElement supertypesObject = parsedObject.get("supertypes");
String[] supertypes = context.deserialize(supertypesObject, String[].class);
JsonElement annotationsObject = parsedObject.get("annotations");
String[] annotations = annotationsObject == null? new String[0] : context.deserialize(annotationsObject, String[].class);
return new Bean(beanName, beanType, location, injectionPoints, supertypes);
return new Bean(beanName, beanType, location, injectionPoints, supertypes, annotations);
}
}

View File

@@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2023 VMware, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.modulith;
import java.util.Collection;
record AppModule(
String name,
String basePackage,
Collection<String> namedInterfaces
) {
}

View File

@@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2023 VMware, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.modulith;
import java.util.List;
public final class AppModules {
private List<AppModule> modules;
public AppModules(List<AppModule> modules) {
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);
}
}

View File

@@ -0,0 +1,212 @@
/*******************************************************************************
* Copyright (c) 2023 VMware, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* VMware, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.vscode.boot.modulith;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.eclipse.lsp4j.TextDocumentIdentifier;
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.IJavaProject;
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
import org.springframework.ide.vscode.commons.java.Version;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
import org.springframework.ide.vscode.commons.protocol.java.Classpath;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.util.FileObserver;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class ModulithService {
private static final Logger log = LoggerFactory.getLogger(ModulithService.class);
private static final List<String> FILE_PATTERNS = List.of("**/*.java");
private Map<IJavaProject, CompletableFuture<AppModules>> cache;
private SpringMetamodelIndex springIndex;
private JavaProjectFinder projectFinder;
public ModulithService(ProjectObserver projectObserver, FileObserver fileObserver, JavaProjectFinder projectFinder, SpringMetamodelIndex springIndex) {
this.projectFinder = projectFinder;
this.springIndex = springIndex;
cache = new ConcurrentHashMap<>();
projectObserver.addListener(new ProjectObserver.Listener() {
@Override
public void deleted(IJavaProject project) {
invalidate(project);
}
@Override
public void created(IJavaProject project) {
invalidate(project);
}
@Override
public void changed(IJavaProject project) {
invalidate(project);
}
});
fileObserver.onFilesCreated(FILE_PATTERNS, this::handleFilesChanged);
fileObserver.onFilesDeleted(FILE_PATTERNS, this::handleFilesChanged);
}
private void handleFilesChanged(String[] files) {
for (String f : files) {
URI uri = URI.create(f);
TextDocumentIdentifier docId = new TextDocumentIdentifier(uri.toASCIIString());
projectFinder.find(docId).ifPresent(this::invalidate);
}
}
private void invalidate(IJavaProject project) {
CompletableFuture<AppModules> future = cache.remove(project);
if (future != null) {
future.cancel(true);
}
}
private CompletableFuture<AppModules> loadModulesMetadata(IJavaProject project) {
Version v = SpringProjectUtil.getDependencyVersion(project, "spring-modulith-core");
if (v != null) {
Set<String> packages = findRootPackages(project);
if (!packages.isEmpty()) {
try {
String javaCmd = ProcessHandle.current().info().command().orElseThrow();
String classpathStr = project.getClasspath().getClasspathEntries().stream().map(cpe -> {
if (Classpath.ENTRY_KIND_SOURCE.equals(cpe.getKind())) {
return cpe.getOutputFolder();
} else {
return cpe.getPath();
}
}).collect(Collectors.joining(System.getProperty("path.separator")));
List<AppModule> allAppModules = new ArrayList<>();
CompletableFuture<?>[] aggregateFuture = packages
.stream()
.map(pkg -> computeAppModules(javaCmd, classpathStr, pkg).thenAccept(oa -> oa.ifPresent(allAppModules::addAll)))
.toArray(CompletableFuture[]::new);
return CompletableFuture.allOf(aggregateFuture).thenApply(r -> new AppModules(allAppModules));
} catch (Exception e) {
log.error("", e);
}
}
}
return CompletableFuture.completedFuture(null);
}
public CompletableFuture<AppModules> getModulesData(IJavaProject project) {
synchronized(project) {
CompletableFuture<AppModules> modules = cache.get(project);
if (modules == null) {
modules = loadModulesMetadata(project);
cache.put(project, modules);
}
return modules;
}
}
private Set<String> findRootPackages(IJavaProject project) {
HashSet<String> packages = new HashSet<>();
for (Bean bean : springIndex.getBeansOfProject(project.getElementName())) {
String beanType = bean.getType();
if (beanType != null) {
if (Arrays.stream(bean.getAnnotations()).anyMatch(Annotations.BOOT_APP::equals)) {
packages.add(getPackageNameFromTypeFQName(beanType));
}
}
}
return packages;
}
static String getPackageNameFromTypeFQName(String fqn) {
int idx = 0;
for (; idx < fqn.length() - 1; idx++) {
char c = fqn.charAt(idx);
if (c == '.' && Character.isUpperCase(fqn.charAt(idx + 1))) {
return fqn.substring(0, idx);
}
}
return fqn;
}
private CompletableFuture<Optional<List<AppModule>>> computeAppModules(String javaCmd, String cp, String pkg) {
try {
Process process = Runtime.getRuntime().exec(new String[] {
javaCmd,
"-cp",
cp,
"org.springframework.modulith.core.util.ApplicationModulesExporter",
pkg
});
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
// skip first line
boolean skipFirstLine = true;
while ((line = reader.readLine()) != null) {
if (skipFirstLine) {
skipFirstLine = false;
} else {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
}
return process.onExit().thenApply(p -> {
String result = builder.toString();
log.info(result);
return Optional.ofNullable(loadAppModules(JsonParser.parseString(result).getAsJsonObject()));
});
} catch (Exception e) {
log.error("", e);
}
return null;
}
private static List<AppModule> loadAppModules(JsonObject json) {
return json.keySet()
.stream()
.map(name -> loadAppModule(name, json.get(name).getAsJsonObject()))
.collect(Collectors.toList());
}
private static AppModule loadAppModule(String name, JsonObject json) {
String basePackage = json.get("basePackage").getAsString();
JsonObject nameInterfacesJson = json.get("namedInterfaces").getAsJsonObject();
List<String> namedInterfaces = nameInterfacesJson.keySet()
.stream()
.flatMap(k -> nameInterfacesJson.get(k).getAsJsonArray().asList().stream().map(js -> js.getAsString()))
.collect(Collectors.toList());
return new AppModule(name, basePackage, namedInterfaces);
}
}

View File

@@ -108,6 +108,12 @@
"label": "Spring factories key not supported",
"description": "Spring factories key not supported",
"defaultSeverity": "ERROR"
},
{
"code": "MODULITH_TYPE_REF_VIOLATION",
"label": "Import from restricted module",
"description": "Restricted dependency",
"defaultSeverity": "ERROR"
}
]
},

View File

@@ -36,6 +36,7 @@ public class SpringMetamodelIndexTest {
private InjectionPoint[] emptyInjectionPoints = new InjectionPoint[0];
private String[] emptySupertypes = new String[0];
private String[] emptyAnnotations = new String[0];
private Location locationForDoc1 = new Location("docURI1", new Range(new Position(1, 1), new Position(1, 10)));
private Location locationForDoc2 = new Location("docURI2", new Range(new Position(2, 1), new Position(2, 10)));
@@ -50,9 +51,9 @@ public class SpringMetamodelIndexTest {
@Test
void testSimpleProjectWithBeansPerProject() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean1, bean2, bean3});
@@ -65,7 +66,7 @@ public class SpringMetamodelIndexTest {
assertTrue(beansList.contains(bean2));
assertTrue(beansList.contains(bean3));
Bean anotherBean = new Bean("anotherBean", "beanType", null, emptyInjectionPoints, emptySupertypes);
Bean anotherBean = new Bean("anotherBean", "beanType", null, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
assertFalse(beansList.contains(anotherBean));
}
@@ -73,9 +74,9 @@ public class SpringMetamodelIndexTest {
@Test
void testSimpleProjectWithBeansPerDocument() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean3 = new Bean("beanWithDifferentName", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean3 = new Bean("beanWithDifferentName", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean1, bean2, bean3});
@@ -100,9 +101,9 @@ public class SpringMetamodelIndexTest {
@Test
void testSimpleProjectWithBeansPerName() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean3 = new Bean("beanWithDifferentName", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean3 = new Bean("beanWithDifferentName", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean1, bean2, bean3});
@@ -121,15 +122,15 @@ public class SpringMetamodelIndexTest {
@Test
void testUpdateBeansForSpecificDoc() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", locationForDoc1.getUri(), new Bean[] {bean1, bean2});
index.updateBeans("someProject", locationForDoc2.getUri(), new Bean[] {bean3});
Bean updatedBean1 = new Bean("updated1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean updatedBean2 = new Bean("updated2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean updatedBean1 = new Bean("updated1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean updatedBean2 = new Bean("updated2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", locationForDoc1.getUri(), new Bean[] {updatedBean1, updatedBean2});
@@ -145,19 +146,19 @@ public class SpringMetamodelIndexTest {
assertFalse(beansList.contains(bean1));
assertFalse(beansList.contains(bean2));
Bean anotherBean = new Bean("anotherBean", "beanType", null, emptyInjectionPoints, emptySupertypes);
Bean anotherBean = new Bean("anotherBean", "beanType", null, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
assertFalse(beansList.contains(anotherBean));
}
@Test
void testUpdateAllBeansForSpecificProject() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean1, bean2});
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean3});
@@ -174,9 +175,9 @@ public class SpringMetamodelIndexTest {
@Test
void testRemoveAllBeansForSpecificProject() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject1", new Bean[] {bean1, bean2});
index.updateBeans("someProject2", new Bean[] {bean3});
@@ -198,9 +199,9 @@ public class SpringMetamodelIndexTest {
@Test
void testRemoveAllBeansForSpecificDocument() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean3 = new Bean("beanName3", "beanType", locationForDoc2, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean1, bean2, bean3});
index.removeBeans("someProject", locationForDoc1.getUri());
@@ -220,7 +221,7 @@ public class SpringMetamodelIndexTest {
InjectionPoint point1 = new InjectionPoint("point1", "point1-type", locationForDoc2);
InjectionPoint point2 = new InjectionPoint("point2", "point2-type", locationForDoc1);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, new InjectionPoint[] {point1, point2}, new String[] {"supertype1", "supertype2"});
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, new InjectionPoint[] {point1, point2}, new String[] {"supertype1", "supertype2"}, emptyAnnotations);
String serialized = bean1.toString();
Gson gson = SymbolCacheOnDisc.createGson();
@@ -248,7 +249,7 @@ public class SpringMetamodelIndexTest {
@Test
void testEmptyInjectionPointsOptimizationWithSerializeDeserializeBeans() {
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
String serialized = bean1.toString();
Gson gson = SymbolCacheOnDisc.createGson();
@@ -263,15 +264,15 @@ public class SpringMetamodelIndexTest {
@Test
void testEmptyInjectionPointsOptimization() {
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
assertSame(DefaultValues.EMPTY_INJECTION_POINTS, bean1.getInjectionPoints());
}
@Test
void testFindNoMatchingBeansWithEmptySupertypes() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean1, bean2});
@@ -285,8 +286,8 @@ public class SpringMetamodelIndexTest {
@Test
void testFindMatchingBeansWithOneProject() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType1", locationForDoc1, emptyInjectionPoints, new String[] {"supertype1", "supertype2"});
Bean bean2 = new Bean("beanName2", "beanType2", locationForDoc1, emptyInjectionPoints, new String[] {"supertype3", "supertype4", "supertype5"});
Bean bean1 = new Bean("beanName1", "beanType1", locationForDoc1, emptyInjectionPoints, new String[] {"supertype1", "supertype2"}, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType2", locationForDoc1, emptyInjectionPoints, new String[] {"supertype3", "supertype4", "supertype5"}, emptyAnnotations);
index.updateBeans("someProject", new Bean[] {bean1, bean2});
@@ -312,11 +313,11 @@ public class SpringMetamodelIndexTest {
@Test
void testFindMatchingBeansWithMultipleProjects() {
SpringMetamodelIndex index = new SpringMetamodelIndex();
Bean bean1 = new Bean("beanName1", "beanType1", locationForDoc1, emptyInjectionPoints, new String[] {"supertype1", "supertype2"});
Bean bean2 = new Bean("beanName2", "beanType2", locationForDoc1, emptyInjectionPoints, new String[] {"supertype3", "supertype4, supertype5"});
Bean bean1 = new Bean("beanName1", "beanType1", locationForDoc1, emptyInjectionPoints, new String[] {"supertype1", "supertype2"}, emptyAnnotations);
Bean bean2 = new Bean("beanName2", "beanType2", locationForDoc1, emptyInjectionPoints, new String[] {"supertype3", "supertype4, supertype5"}, emptyAnnotations);
Bean bean3 = new Bean("beanName3", "beanType1", locationForDoc1, emptyInjectionPoints, new String[] {"supertype1", "supertype2"});
Bean bean4 = new Bean("beanName4", "beanType2", locationForDoc1, emptyInjectionPoints, new String[] {"supertype3", "supertype4, supertype5"});
Bean bean3 = new Bean("beanName3", "beanType1", locationForDoc1, emptyInjectionPoints, new String[] {"supertype1", "supertype2"}, emptyAnnotations);
Bean bean4 = new Bean("beanName4", "beanType2", locationForDoc1, emptyInjectionPoints, new String[] {"supertype3", "supertype4, supertype5"}, emptyAnnotations);
index.updateBeans("projectA", new Bean[] {bean1, bean2});
index.updateBeans("projectB", new Bean[] {bean3, bean4});

View File

@@ -494,6 +494,18 @@
"HINT",
"ERROR"
]
},
"spring-boot.ls.problem.boot3.MODULITH_TYPE_REF_VIOLATION": {
"type": "string",
"default": "ERROR",
"description": "Restricted dependency",
"enum": [
"IGNORE",
"INFO",
"WARNING",
"HINT",
"ERROR"
]
}
}
},
@@ -1021,4 +1033,4 @@
"extensionDependencies": [
"redhat.java"
]
}
}