service method to return beans as future waits for project-related indexing tasks to complete

This commit is contained in:
Martin Lippert
2023-04-18 17:47:25 +02:00
parent 825a65d1df
commit aac57fd120
2 changed files with 127 additions and 9 deletions

View File

@@ -98,10 +98,11 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
private final List<EnhancedSymbolInformation> symbols = new ArrayList<>();
private final ConcurrentMap<String, List<EnhancedSymbolInformation>> symbolsByDoc = new ConcurrentHashMap<>();
private final ConcurrentMap<String, List<EnhancedSymbolInformation>> symbolsByProject = new ConcurrentHashMap<>();
private final ExecutorService updateQueue = Executors.newSingleThreadExecutor();
private final Map<String, CompletableFuture<Void>> latestScheduledTaskByProject = new ConcurrentHashMap<String, CompletableFuture<Void>>();
private SpringIndexer[] indexers;
private ListenerList<Void> listeners = new ListenerList<Void>();
@@ -343,6 +344,7 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
cf.thenAccept( f -> {
projectInitializedFuture(project).complete(null);
});
return cf;
}
@@ -365,7 +367,10 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
}
CompletableFuture<Void> future = CompletableFuture.allOf(futures);
future.thenAccept(v -> listeners.fire(v));
this.latestScheduledTaskByProject.put(project.getElementName(), future);
return future;
}
} else {
@@ -389,7 +394,10 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
return CompletableFuture.completedFuture(null);
} else {
DeleteProject initializeItem = new DeleteProject(project, this.indexers);
return CompletableFuture.runAsync(initializeItem, this.updateQueue);
CompletableFuture<Void> future = CompletableFuture.runAsync(initializeItem, this.updateQueue);
this.latestScheduledTaskByProject.put(project.getElementName(), future);
return future;
}
} catch (Throwable e) {
log.error("", e);
@@ -545,7 +553,10 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
Optional<IJavaProject> maybeProject = projectFinder().find(new TextDocumentIdentifier(deletedDocURI));
if (maybeProject.isPresent()) {
DeleteItems deleteItem = new DeleteItems(maybeProject.get(), new String[] {deletedDocURI}, this.indexers);
return CompletableFuture.runAsync(deleteItem, this.updateQueue);
CompletableFuture<Void> future = CompletableFuture.runAsync(deleteItem, this.updateQueue);
this.latestScheduledTaskByProject.put(maybeProject.get().getElementName(), future);
return future;
}
}
catch (Exception e) {
@@ -569,7 +580,10 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
List<String> docURIs = projectMapping.get(project);
DeleteItems deleteItems = new DeleteItems(project, (String[]) docURIs.toArray(new String[docURIs.size()]), this.indexers);
futures.add(CompletableFuture.runAsync(deleteItems, this.updateQueue));
CompletableFuture<Void> future = CompletableFuture.runAsync(deleteItems, this.updateQueue);
this.latestScheduledTaskByProject.put(project.getElementName(), future);
futures.add(future);
}
CompletableFuture<Void> future = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()]));
@@ -691,8 +705,16 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
}
@Override
public CompletableFuture<List<Bean>> beans(String project) {
return CompletableFuture.completedFuture(Arrays.asList(springIndex.getBeansOfProject(project)));
public CompletableFuture<List<Bean>> beans(String projectName) {
CompletableFuture<Void> latestTask = this.latestScheduledTaskByProject.get(projectName);
if (latestTask != null) {
return latestTask.thenApply((e) -> Arrays.asList(springIndex.getBeansOfProject(projectName)));
}
else {
return CompletableFuture.completedFuture(null);
}
}
/**
@@ -782,8 +804,7 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
CompletableFuture<Void> updateItem(IJavaProject project, DocumentDescriptor updatedDoc, String content, SpringIndexer indexer) {
log.debug("scheduling updateItem {}. {}, {}, {}", project.getElementName(), updatedDoc.getDocURI(), updatedDoc.getLastModified(), indexer);
return CompletableFuture.runAsync(() -> {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
log.debug("updateItem {}. {}, {}, {}", project.getElementName(), updatedDoc.getDocURI(), updatedDoc.getLastModified(), indexer);
indexer.updateFile(project, updatedDoc, content);
@@ -791,6 +812,9 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
log.error("{}", e);
}
}, this.updateQueue);
this.latestScheduledTaskByProject.put(project.getElementName(), future);
return future;
}
CompletableFuture<Void> updateItems(IJavaProject project, DocumentDescriptor[] updatedDoc, SpringIndexer indexer) {
@@ -798,7 +822,7 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
log.debug("scheduling updateItem {}. {}, {}, {}", project.getElementName(), doc.getDocURI(), doc.getLastModified(), indexer);
}
return CompletableFuture.runAsync(() -> {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
for (DocumentDescriptor doc : updatedDoc) {
@@ -810,6 +834,9 @@ public class SpringSymbolIndex implements InitializingBean, SpringModelService {
log.error("{}", e);
}
}, this.updateQueue);
this.latestScheduledTaskByProject.put(project.getElementName(), future);
return future;
}
private class DeleteItems implements Runnable {

View File

@@ -0,0 +1,91 @@
/*******************************************************************************
* 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.metamodel.test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.ide.vscode.boot.app.SpringSymbolIndex;
import org.springframework.ide.vscode.boot.bootiful.BootLanguageServerTest;
import org.springframework.ide.vscode.boot.bootiful.SymbolProviderTestConf;
import org.springframework.ide.vscode.boot.index.SpringMetamodelIndex;
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.InjectionPoint;
import org.springframework.ide.vscode.project.harness.BootLanguageServerHarness;
import org.springframework.ide.vscode.project.harness.ProjectsHarness;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* @author Martin Lippert
*/
@ExtendWith(SpringExtension.class)
@BootLanguageServerTest
@Import(SymbolProviderTestConf.class)
public class SpringModelServiceTest {
@Autowired private BootLanguageServerHarness harness;
@Autowired private JavaProjectFinder projectFinder;
@Autowired private SpringSymbolIndex indexer;
private File directory;
@BeforeEach
public void setup() throws Exception {
harness.intialize(null);
directory = new File(ProjectsHarness.class.getResource("/test-projects/test-spring-indexing/").toURI());
String projectDir = directory.toURI().toString();
// trigger project creation
projectFinder.find(new TextDocumentIdentifier(projectDir)).get();
// CompletableFuture<Void> initProject = indexer.waitOperation();
// initProject.get(5, TimeUnit.SECONDS);
}
@Test
void testSpringModelServiceBeansForNonExistingProject() throws Exception {
CompletableFuture<List<Bean>> result = indexer.beans("random-project");
List<Bean> list = result.get();
assertNull(list);
}
@Test
void testBeansNameAndTypeFromBeanAnnotatedMethod() throws Exception {
CompletableFuture<List<Bean>> result = indexer.beans("test-spring-indexing");
List<Bean> beans = result.get(5, TimeUnit.SECONDS);
assertNotNull(beans);
assertEquals(11, beans.size());
}
}