GH-1049: spring index protocol now has new request for find matching beans

This commit is contained in:
Martin Lippert
2023-05-24 13:12:50 +02:00
parent c9b526f2f6
commit 709e215e8c
8 changed files with 173 additions and 8 deletions

View File

@@ -99,6 +99,7 @@ import org.springframework.ide.vscode.commons.languageserver.reconcile.Reconcile
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.BeansParams;
import org.springframework.ide.vscode.commons.protocol.spring.MatchingBeansParams;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndex;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndexLanguageServer;
import org.springframework.ide.vscode.commons.util.Assert;
@@ -927,13 +928,14 @@ public final class SimpleLanguageServer implements Sts4LanguageServer, SpringInd
@Override
public CompletableFuture<List<Bean>> beans(BeansParams params) {
Map<String, SpringIndex> allSpringIndex = appContext.getBeansOfType(SpringIndex.class, false, false);
Assert.isLegal(allSpringIndex.size() <= 1, "One at most SpringModel bean is expected");
if (!allSpringIndex.isEmpty()) {
SpringIndex springIndex = allSpringIndex.values().iterator().next();
return springIndex.beans(params);
}
return CompletableFuture.completedFuture(Collections.emptyList());
SpringIndex springIndex = appContext.getBean(SpringIndex.class);
return springIndex.beans(params);
}
@Override
public CompletableFuture<List<Bean>> matchingBeans(MatchingBeansParams params) {
SpringIndex springIndex = appContext.getBean(SpringIndex.class);
return springIndex.matchingBeans(params);
}
}

View File

@@ -0,0 +1,25 @@
/*******************************************************************************
* 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.commons.protocol.spring;
public class MatchingBeansParams extends BeansParams {
private String beanType;
public String getBeanTypeToMatch() {
return beanType;
}
public void setBeanTypeToMatch(String beanType) {
this.beanType = beanType;
}
}

View File

@@ -16,5 +16,6 @@ import java.util.concurrent.CompletableFuture;
public interface SpringIndex {
CompletableFuture<List<Bean>> beans(BeansParams params);
CompletableFuture<List<Bean>> matchingBeans(MatchingBeansParams params);
}

View File

@@ -22,5 +22,10 @@ public interface SpringIndexLanguageServer extends LanguageServer {
default CompletableFuture<List<Bean>> beans(BeansParams params) {
throw new UnsupportedOperationException();
}
@JsonRequest("spring/index/beans/matches")
default CompletableFuture<List<Bean>> matchingBeans(MatchingBeansParams params) {
throw new UnsupportedOperationException();
}
}

View File

@@ -71,6 +71,7 @@ import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocu
import org.springframework.ide.vscode.commons.languageserver.util.SimpleWorkspaceService;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.BeansParams;
import org.springframework.ide.vscode.commons.protocol.spring.MatchingBeansParams;
import org.springframework.ide.vscode.commons.protocol.spring.SpringIndex;
import org.springframework.ide.vscode.commons.util.Futures;
import org.springframework.ide.vscode.commons.util.StringUtil;
@@ -720,7 +721,21 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex {
else {
return CompletableFuture.completedFuture(null);
}
}
@Override
public CompletableFuture<List<Bean>> matchingBeans(MatchingBeansParams params) {
String projectName = params.getProjectName();
String matchType = params.getBeanTypeToMatch();
CompletableFuture<Void> latestTask = this.latestScheduledTaskByProject.get(projectName);
if (latestTask != null) {
return latestTask.thenApply((e) -> Arrays.asList(springIndex.getMatchingBeans(projectName, matchType)));
}
else {
return CompletableFuture.completedFuture(null);
}
}
/**
@@ -989,4 +1004,5 @@ public class SpringSymbolIndex implements InitializingBean, SpringIndex {
public void onUpdate(Consumer<Void> listener) {
listeners.add(listener);
}
}

View File

@@ -97,4 +97,15 @@ public class SpringMetamodelIndex {
}
}
public Bean[] getMatchingBeans(String projectName, String matchType) {
Bean[] allBeans = this.beansPerProject.get(projectName);
if (allBeans != null) {
return Arrays.stream(allBeans).filter(bean -> bean.isTypeCompatibleWith(matchType)).collect(Collectors.toList()).toArray(new Bean[0]);
}
else {
return null;
}
}
}

View File

@@ -31,6 +31,7 @@ import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
import org.springframework.ide.vscode.commons.protocol.spring.Bean;
import org.springframework.ide.vscode.commons.protocol.spring.BeansParams;
import org.springframework.ide.vscode.commons.protocol.spring.MatchingBeansParams;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -86,4 +87,35 @@ public class SpringIndexViaLSPMethodTest {
assertEquals(11, beans.size());
}
@Test
void testMatchingBeansForObject() throws Exception {
MatchingBeansParams params = new MatchingBeansParams();
params.setProjectName("test-spring-indexing");
params.setBeanTypeToMatch("java.lang.Object");
CompletableFuture<List<Bean>> result = indexer.matchingBeans(params);
List<Bean> beans = result.get(5, TimeUnit.SECONDS);
assertNotNull(beans);
assertEquals(10, beans.size());
}
@Test
void testMatchingBeansForSpecificSupertype() throws Exception {
MatchingBeansParams params = new MatchingBeansParams();
params.setProjectName("test-spring-indexing");
params.setBeanTypeToMatch("org.test.springdata.CustomerRepository");
CompletableFuture<List<Bean>> result = indexer.matchingBeans(params);
List<Bean> beans = result.get(5, TimeUnit.SECONDS);
assertNotNull(beans);
assertEquals(1, beans.size());
assertEquals("customerRepository", beans.get(0).getName());
assertEquals("org.test.springdata.CustomerRepository", beans.get(0).getType());
}
}

View File

@@ -266,5 +266,78 @@ public class SpringMetamodelIndexTest {
Bean bean1 = new Bean("beanName1", "beanType", locationForDoc1, emptyInjectionPoints, emptySupertypes);
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);
index.updateBeans("someProject", new Bean[] {bean1, bean2});
Bean[] matchingBeans = index.getMatchingBeans("someProject", "");
assertEquals(0, matchingBeans.length);
matchingBeans = index.getMatchingBeans("someProject", "sometype");
assertEquals(0, matchingBeans.length);
}
@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"});
index.updateBeans("someProject", new Bean[] {bean1, bean2});
Bean[] matchingBeans = index.getMatchingBeans("someProject", "supertype2");
assertEquals(1, matchingBeans.length);
assertSame(bean1, matchingBeans[0]);
matchingBeans = index.getMatchingBeans("someProject", "beanType1");
assertEquals(1, matchingBeans.length);
assertSame(bean1, matchingBeans[0]);
matchingBeans = index.getMatchingBeans("someProject", "supertype5");
assertEquals(1, matchingBeans.length);
assertSame(bean2, matchingBeans[0]);
matchingBeans = index.getMatchingBeans("someProject", "sometype");
assertEquals(0, matchingBeans.length);
matchingBeans = index.getMatchingBeans("otherProject", "supertype1");
assertNull(matchingBeans);
}
@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 bean3 = new Bean("beanName3", "beanType1", locationForDoc1, emptyInjectionPoints, new String[] {"supertype1", "supertype2"});
Bean bean4 = new Bean("beanName4", "beanType2", locationForDoc1, emptyInjectionPoints, new String[] {"supertype3", "supertype4, supertype5"});
index.updateBeans("projectA", new Bean[] {bean1, bean2});
index.updateBeans("projectB", new Bean[] {bean3, bean4});
Bean[] matchingBeans = index.getMatchingBeans("projectA", "supertype2");
assertEquals(1, matchingBeans.length);
assertSame(bean1, matchingBeans[0]);
matchingBeans = index.getMatchingBeans("projectA", "beanType2");
assertEquals(1, matchingBeans.length);
assertSame(bean2, matchingBeans[0]);
matchingBeans = index.getMatchingBeans("projectB", "supertype2");
assertEquals(1, matchingBeans.length);
assertSame(bean3, matchingBeans[0]);
matchingBeans = index.getMatchingBeans("projectB", "beanType2");
assertEquals(1, matchingBeans.length);
assertSame(bean4, matchingBeans[0]);
matchingBeans = index.getMatchingBeans("otherProject", "supertype1");
}
}