GH-1041: removed feign-specific symbol addon information and rebased implementation on spring index
This commit is contained in:
@@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.cron.CronExpressionsInlayHintsProvider;
|
||||
import org.springframework.ide.vscode.boot.java.cron.CronReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.cron.CronSemanticTokens;
|
||||
@@ -63,8 +64,8 @@ public class JdtConfig {
|
||||
return new BeanMethodNotPublicReconciler(server.getQuickfixRegistry());
|
||||
}
|
||||
|
||||
@Bean AddConfigurationIfBeansPresentReconciler addConfigurationIfBeansPresentReconciler(SimpleLanguageServer server) {
|
||||
return new AddConfigurationIfBeansPresentReconciler(server.getQuickfixRegistry());
|
||||
@Bean AddConfigurationIfBeansPresentReconciler addConfigurationIfBeansPresentReconciler(SimpleLanguageServer server, SpringMetamodelIndex springIndex) {
|
||||
return new AddConfigurationIfBeansPresentReconciler(server.getQuickfixRegistry(), springIndex);
|
||||
}
|
||||
|
||||
@Bean AutowiredFieldIntoConstructorParameterReconciler autowiredFieldIntoConstructorParameterReconciler(SimpleLanguageServer server) {
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* 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.beans;
|
||||
|
||||
public class FeignClientBeanSymbolAddOnInformation extends BeansSymbolAddOnInformation {
|
||||
|
||||
final public String[] configClasses;
|
||||
|
||||
public FeignClientBeanSymbolAddOnInformation(String beanID, String beanType, String... configClasses) {
|
||||
super(beanID, beanType);
|
||||
this.configClasses = configClasses;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,20 +13,16 @@ package org.springframework.ide.vscode.boot.java.beans;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
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.ArrayInitializer;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.NormalAnnotation;
|
||||
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.jdt.core.dom.TypeLiteral;
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
@@ -86,7 +82,7 @@ public class FeignClientSymbolProvider extends AbstractSymbolProvider {
|
||||
beanLabel("+", annotationTypeName, metaAnnotationNames, beanName, beanType == null ? "" : beanType.getName()), SymbolKind.Interface,
|
||||
Either.forLeft(location));
|
||||
|
||||
SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new FeignClientBeanSymbolAddOnInformation(beanName, beanType == null ? "" : beanType.getQualifiedName(), getConfigClass(node))};
|
||||
SymbolAddOnInformation[] addon = new SymbolAddOnInformation[] {new BeansSymbolAddOnInformation(beanName, beanType == null ? "" : beanType.getQualifiedName())};
|
||||
|
||||
InjectionPoint[] injectionPoints = ASTUtils.findInjectionPoints(type, doc);
|
||||
|
||||
@@ -159,31 +155,4 @@ public class FeignClientSymbolProvider extends AbstractSymbolProvider {
|
||||
return BeanUtils.getBeanNameFromType(typeDecl.getName().getIdentifier());
|
||||
}
|
||||
|
||||
private String[] getConfigClass(Annotation node) {
|
||||
if (node.isNormalAnnotation()) {
|
||||
NormalAnnotation normalAnnotation = (NormalAnnotation) node;
|
||||
for (Object o : normalAnnotation.values()) {
|
||||
if (o instanceof MemberValuePair) {
|
||||
MemberValuePair pair = (MemberValuePair) o;
|
||||
if ("configuration".equals(pair.getName().getIdentifier())) {
|
||||
if (pair.getValue() instanceof TypeLiteral) {
|
||||
ITypeBinding b = ((TypeLiteral) pair.getValue()).getType().resolveBinding();
|
||||
return new String[] { b.getQualifiedName() };
|
||||
} else if (pair.getValue() instanceof ArrayInitializer){
|
||||
List<?> expressions = (List<?>) ((ArrayInitializer) pair.getValue()).expressions();
|
||||
return expressions.stream()
|
||||
.filter(TypeLiteral.class::isInstance)
|
||||
.map(TypeLiteral.class::cast)
|
||||
.map(tl -> tl.getType().resolveBinding())
|
||||
.filter(Objects::nonNull)
|
||||
.map(b -> b.getQualifiedName())
|
||||
.toArray(String[]::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ package org.springframework.ide.vscode.boot.java.reconcilers;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
@@ -23,17 +25,10 @@ import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.openrewrite.java.spring.boot2.AddConfigurationAnnotationIfBeansPresent;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.annotations.AnnotationHierarchies;
|
||||
import org.springframework.ide.vscode.boot.java.beans.BeansSymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.beans.FeignClientBeanSymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.commons.Version;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
@@ -41,19 +36,21 @@ import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRe
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.IProblemCollector;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ProblemType;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblemImpl;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
import org.springframework.ide.vscode.commons.rewrite.config.RecipeScope;
|
||||
import org.springframework.ide.vscode.commons.rewrite.java.FixDescriptor;
|
||||
|
||||
public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconciler, ApplicationContextAware {
|
||||
public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconciler {
|
||||
|
||||
private static final String PROBLEM_LABEL = "'@Configuration' is missing on a class defining Spring Beans";
|
||||
private static final String FIX_LABEL = "Add missing '@Configuration' annotations over classes";
|
||||
|
||||
private QuickfixRegistry quickfixRegistry;
|
||||
private ApplicationContext applicationContext;
|
||||
private final QuickfixRegistry quickfixRegistry;
|
||||
private final SpringMetamodelIndex springIndex;
|
||||
|
||||
public AddConfigurationIfBeansPresentReconciler(QuickfixRegistry quickfixRegistry) {
|
||||
public AddConfigurationIfBeansPresentReconciler(QuickfixRegistry quickfixRegistry, SpringMetamodelIndex springIndex) {
|
||||
this.quickfixRegistry = quickfixRegistry;
|
||||
this.springIndex = springIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,11 +64,6 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile
|
||||
return Boot2JavaProblemType.MISSING_CONFIGURATION_ANNOTATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ASTVisitor createVisitor(IJavaProject project, URI docUri, CompilationUnit cu, IProblemCollector problemCollector, boolean isCompleteAst) {
|
||||
return new ASTVisitor() {
|
||||
@@ -146,33 +138,34 @@ public class AddConfigurationIfBeansPresentReconciler implements JdtAstReconcile
|
||||
}
|
||||
|
||||
private boolean isException(IJavaProject project, TypeDeclaration classDecl) {
|
||||
if (applicationContext != null) {
|
||||
SpringSymbolIndex index = applicationContext.getBean(SpringSymbolIndex.class);
|
||||
if (index != null) {
|
||||
final String beanClassName = ReconcileUtils.getDeepErasureType(classDecl.resolveBinding()).getQualifiedName();
|
||||
for (EnhancedSymbolInformation s : index.getEnhancedSymbols(project)) {
|
||||
SymbolAddOnInformation[] additionalInformation = s.getAdditionalInformation();
|
||||
if (additionalInformation != null) {
|
||||
for (SymbolAddOnInformation info : additionalInformation) {
|
||||
if (info instanceof BeansSymbolAddOnInformation) {
|
||||
BeansSymbolAddOnInformation info2 = (BeansSymbolAddOnInformation) info;
|
||||
if (beanClassName.equals(info2.getBeanType())) {
|
||||
return true;
|
||||
}
|
||||
if (info instanceof FeignClientBeanSymbolAddOnInformation) {
|
||||
FeignClientBeanSymbolAddOnInformation feign = (FeignClientBeanSymbolAddOnInformation) info;
|
||||
for (String configBean : feign.configClasses) {
|
||||
if (beanClassName.equals(configBean)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (springIndex == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String beanClassName = ReconcileUtils.getDeepErasureType(classDecl.resolveBinding()).getQualifiedName();
|
||||
|
||||
Bean[] beans = this.springIndex.getBeansOfProject(project.getElementName());
|
||||
|
||||
if (beans == null || beans.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// look for beans with that type
|
||||
if (Arrays.stream(beans).anyMatch(bean -> bean.getType().equals(beanClassName))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isConfiguredAsfeignConfigClass = Arrays.stream(beans)
|
||||
.flatMap(bean -> Arrays.stream(bean.getAnnotations()))
|
||||
.filter(annotation -> annotation.getAnnotationType().equals(Annotations.FEIGN_CLIENT))
|
||||
.map(annotation -> annotation.getAttributes().get("configuration"))
|
||||
.flatMap(attributeValues -> attributeValues != null ? Arrays.stream(attributeValues) : Stream.empty())
|
||||
.anyMatch(attributeValue -> attributeValue.getName().equals(beanClassName));
|
||||
|
||||
if (isConfiguredAsfeignConfigClass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public class SpringIndexerJava implements SpringIndexer {
|
||||
|
||||
// whenever the implementation of the indexer changes in a way that the stored data in the cache is no longer valid,
|
||||
// we need to change the generation - this will result in a re-indexing due to no up-to-date cache data being found
|
||||
private static final String GENERATION = "GEN-8";
|
||||
private static final String GENERATION = "GEN-9";
|
||||
private static final String INDEX_FILES_TASK_ID = "index-java-source-files-task-";
|
||||
|
||||
private static final String SYMBOL_KEY = "symbols";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2023 VMware, Inc.
|
||||
* Copyright (c) 2023, 2024 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
|
||||
@@ -11,32 +11,23 @@
|
||||
package org.springframework.ide.vscode.boot.java.reconcilers.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.lsp4j.Location;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.eclipse.lsp4j.SymbolKind;
|
||||
import org.eclipse.lsp4j.WorkspaceSymbol;
|
||||
import org.eclipse.lsp4j.jsonrpc.messages.Either;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
|
||||
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
|
||||
import org.springframework.ide.vscode.boot.java.Annotations;
|
||||
import org.springframework.ide.vscode.boot.java.Boot2JavaProblemType;
|
||||
import org.springframework.ide.vscode.boot.java.beans.ConfigBeanSymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.beans.FeignClientBeanSymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.EnhancedSymbolInformation;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.SymbolAddOnInformation;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.AddConfigurationIfBeansPresentReconciler;
|
||||
import org.springframework.ide.vscode.boot.java.reconcilers.JdtAstReconciler;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.quickfix.QuickfixRegistry;
|
||||
import org.springframework.ide.vscode.commons.languageserver.reconcile.ReconcileProblem;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationAttributeValue;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.AnnotationMetadata;
|
||||
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
|
||||
|
||||
public class AddConfigurationIfBeansPresentReconcilerTest extends BaseReconcilerTest {
|
||||
|
||||
@@ -52,7 +43,7 @@ public class AddConfigurationIfBeansPresentReconcilerTest extends BaseReconciler
|
||||
|
||||
@Override
|
||||
protected JdtAstReconciler getReconciler() {
|
||||
return new AddConfigurationIfBeansPresentReconciler(new QuickfixRegistry());
|
||||
return new AddConfigurationIfBeansPresentReconciler(new QuickfixRegistry(), null);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
@@ -81,7 +72,11 @@ public class AddConfigurationIfBeansPresentReconcilerTest extends BaseReconciler
|
||||
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile("A.java", source, false);
|
||||
List<ReconcileProblem> problems = reconcile(() -> {
|
||||
SpringMetamodelIndex springIndex = new SpringMetamodelIndex();
|
||||
AddConfigurationIfBeansPresentReconciler r = new AddConfigurationIfBeansPresentReconciler(new QuickfixRegistry(), springIndex);
|
||||
return r;
|
||||
}, "A.java", source, false);
|
||||
|
||||
assertEquals(1, problems.size());
|
||||
|
||||
@@ -93,7 +88,6 @@ public class AddConfigurationIfBeansPresentReconcilerTest extends BaseReconciler
|
||||
assertEquals("A", markedStr);
|
||||
|
||||
assertEquals(2, problem.getQuickfixes().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,26 +107,20 @@ public class AddConfigurationIfBeansPresentReconcilerTest extends BaseReconciler
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile(() -> {
|
||||
AddConfigurationIfBeansPresentReconciler r = new AddConfigurationIfBeansPresentReconciler(new QuickfixRegistry());
|
||||
|
||||
WorkspaceSymbol workspaceSymbol = new WorkspaceSymbol("testConfig", SymbolKind.Class, Either.forLeft(new Location("file:///someUri", new Range())));
|
||||
ConfigBeanSymbolAddOnInformation beanSymbolAddOn = new ConfigBeanSymbolAddOnInformation("a", "example.demo.A");
|
||||
EnhancedSymbolInformation beanSymbol = new EnhancedSymbolInformation(workspaceSymbol, new SymbolAddOnInformation[] { beanSymbolAddOn });
|
||||
|
||||
SpringSymbolIndex mockSymbolIndex = mock(SpringSymbolIndex.class);
|
||||
|
||||
when(mockSymbolIndex.getEnhancedSymbols(any(IJavaProject.class))).thenReturn(List.of(beanSymbol));
|
||||
|
||||
ApplicationContext context = mock(ApplicationContext.class);
|
||||
when(context.getBean(SpringSymbolIndex.class)).thenReturn(mockSymbolIndex);
|
||||
|
||||
r.setApplicationContext(context);
|
||||
SpringMetamodelIndex springIndex = new SpringMetamodelIndex();
|
||||
|
||||
AnnotationMetadata annotationMetadata = new AnnotationMetadata(Annotations.CONFIGURATION, false, null, Map.of());
|
||||
AnnotationMetadata[] annotations = new AnnotationMetadata[] {annotationMetadata};
|
||||
Bean configBean = new Bean("a", "example.demo.A", null, null, null, annotations);
|
||||
Bean[] beans = new Bean[] {configBean};
|
||||
springIndex.updateBeans(getProjectName(), beans);
|
||||
|
||||
AddConfigurationIfBeansPresentReconciler r = new AddConfigurationIfBeansPresentReconciler(new QuickfixRegistry(), springIndex);
|
||||
|
||||
return r;
|
||||
}, "A.java", source, false);
|
||||
|
||||
assertEquals(0, problems.size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -152,25 +140,19 @@ public class AddConfigurationIfBeansPresentReconcilerTest extends BaseReconciler
|
||||
}
|
||||
""";
|
||||
List<ReconcileProblem> problems = reconcile(() -> {
|
||||
AddConfigurationIfBeansPresentReconciler r = new AddConfigurationIfBeansPresentReconciler(new QuickfixRegistry());
|
||||
SpringMetamodelIndex springIndex = new SpringMetamodelIndex();
|
||||
|
||||
WorkspaceSymbol workspaceSymbol = new WorkspaceSymbol("testConfig", SymbolKind.Class, Either.forLeft(new Location("file:///someUri", new Range())));
|
||||
FeignClientBeanSymbolAddOnInformation beanSymbolAddOn = new FeignClientBeanSymbolAddOnInformation("b", "example.demo.B", "example.demo.A");
|
||||
EnhancedSymbolInformation beanSymbol = new EnhancedSymbolInformation(workspaceSymbol, new SymbolAddOnInformation[] { beanSymbolAddOn });
|
||||
AnnotationMetadata annotationMetadata = new AnnotationMetadata(Annotations.FEIGN_CLIENT, false, null, Map.of("configuration", new AnnotationAttributeValue[] {new AnnotationAttributeValue("example.demo.A", null)}));
|
||||
AnnotationMetadata[] annotations = new AnnotationMetadata[] {annotationMetadata};
|
||||
Bean configBean = new Bean("feignClient", "example.demo.FeignClientExample", null, null, null, annotations);
|
||||
Bean[] beans = new Bean[] {configBean};
|
||||
springIndex.updateBeans(getProjectName(), beans);
|
||||
|
||||
SpringSymbolIndex mockSymbolIndex = mock(SpringSymbolIndex.class);
|
||||
|
||||
when(mockSymbolIndex.getEnhancedSymbols(any(IJavaProject.class))).thenReturn(List.of(beanSymbol));
|
||||
|
||||
ApplicationContext context = mock(ApplicationContext.class);
|
||||
when(context.getBean(SpringSymbolIndex.class)).thenReturn(mockSymbolIndex);
|
||||
|
||||
r.setApplicationContext(context);
|
||||
AddConfigurationIfBeansPresentReconciler r = new AddConfigurationIfBeansPresentReconciler(new QuickfixRegistry(), springIndex);
|
||||
|
||||
return r;
|
||||
}, "A.java", source, false);
|
||||
|
||||
assertEquals(0, problems.size());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user