diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java index fe3e5b074..6e6f1ad2f 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/java/JdtLsJavaProject.java @@ -13,20 +13,23 @@ package org.springframework.ide.vscode.commons.java; import java.net.URI; import org.springframework.ide.vscode.commons.jdtls.JdtLsIndex; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient; public class JdtLsJavaProject extends AbstractJavaProject { final private STS4LanguageClient client; + final private ProjectObserver projectObserver; - public JdtLsJavaProject(STS4LanguageClient client, URI uri, IClasspath classpath) { + public JdtLsJavaProject(STS4LanguageClient client, URI uri, IClasspath classpath, ProjectObserver projectObserver) { super(uri, classpath); this.client = client; + this.projectObserver = projectObserver; } @Override protected ClasspathIndex createIndex() { - return new JdtLsIndex(client, getLocationUri()); + return new JdtLsIndex(client, getLocationUri(), projectObserver); } } diff --git a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jdtls/JdtLsIndex.java b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jdtls/JdtLsIndex.java index 83f147b4b..743661683 100644 --- a/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jdtls/JdtLsIndex.java +++ b/headless-services/commons/commons-java/src/main/java/org/springframework/ide/vscode/commons/jdtls/JdtLsIndex.java @@ -23,10 +23,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.commons.java.ClasspathIndex; import org.springframework.ide.vscode.commons.java.IJavaModuleData; +import org.springframework.ide.vscode.commons.java.IJavaProject; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.java.JavaUtils; import org.springframework.ide.vscode.commons.javadoc.JdtLsJavadocProvider; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver.Listener; import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient; +import org.springframework.ide.vscode.commons.protocol.java.Classpath; import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams; import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams; import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams; @@ -46,26 +50,52 @@ import reactor.util.function.Tuples; public class JdtLsIndex implements ClasspathIndex { - private static final long SEARCH_TIMEOUT = 700; + final private static long SEARCH_TIMEOUT = 700; - private static final Logger log = LoggerFactory.getLogger(JdtLsIndex.class); + final private static Logger log = LoggerFactory.getLogger(JdtLsIndex.class); - private final STS4LanguageClient client; - private final URI projectUri; - private final JdtLsJavadocProvider javadocProvider; + final private STS4LanguageClient client; + final private URI projectUri; + final private JdtLsJavadocProvider javadocProvider; + final private ProjectObserver projectObserver; + + final private Cache> binaryTypeCache = CacheBuilder.newBuilder().build(); + final private Cache> sourceTypeCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build(); - final private Cache> typeCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build(); final private Cache>> supertypesCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build(); final private Cache>> subtypesCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.SECONDS).build(); - public JdtLsIndex(STS4LanguageClient client, URI projectUri) { + final private Listener projectListener = new Listener() { + + @Override + public void deleted(IJavaProject project) { + // ignore + } + + @Override + public void created(IJavaProject project) { + // ignore + } + + @Override + public void changed(IJavaProject project) { + binaryTypeCache.invalidateAll(); + sourceTypeCache.invalidateAll(); + } + }; + + public JdtLsIndex(STS4LanguageClient client, URI projectUri, ProjectObserver projectObserver) { this.client = client; this.projectUri = projectUri; + this.projectObserver = projectObserver; this.javadocProvider = new JdtLsJavadocProvider(client, projectUri.toString()); + + this.projectObserver.addListener(projectListener); } @Override public void dispose() { + projectObserver.removeListener(projectListener); } private IType toType(TypeData data) { @@ -80,25 +110,38 @@ public class JdtLsIndex implements ClasspathIndex { return Wrappers.wrap(data, Suppliers.memoize(() -> findType(data.getFqName())), Suppliers.memoize(() -> declaringTypeFqName == null ? null : findType(declaringTypeFqName)), javadocProvider); } + private TypeData findTypeData(String fqName) { + JavaDataParams params = new JavaDataParams(projectUri.toString(), "L" + fqName.replace('.', '/') + ";", false); + try { + return client.javaType(params).get(); + } catch (InterruptedException | ExecutionException e) { + log.error("", e); + } + return null; + } + @Override public IType findType(String fqName) { - try { - return typeCache.get(fqName, () -> { - JavaDataParams params = new JavaDataParams(projectUri.toString(), "L" + fqName.replace('.', '/') + ";", false); - try { - TypeData data = client.javaType(params).get(); - if (data != null) { - return Optional.ofNullable(toType(data)); - } - } catch (InterruptedException | ExecutionException e) { - log.error("", e); - } - return Optional.empty(); - }).orElse(null); - } catch (ExecutionException e) { - log.error("{}", e); - return null; + Optional type = binaryTypeCache.getIfPresent(fqName); + if (type == null) { + type = sourceTypeCache.getIfPresent(fqName); } + + if (type == null) { + TypeData data = findTypeData(fqName); + if (data == null) { + type = Optional.empty(); + sourceTypeCache.put(fqName, type); + } else { + type = Optional.of(toType(data)); + if (Classpath.isBinary(data.getClasspathEntry().getCpe())) { + binaryTypeCache.put(fqName, type); + } else { + sourceTypeCache.put(fqName, type); + } + } + } + return type.orElse(null); } @Override diff --git a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JdtLsIndexTest.java b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JdtLsIndexTest.java index ac75771a3..4f4e96df6 100644 --- a/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JdtLsIndexTest.java +++ b/headless-services/commons/commons-java/src/test/java/org/springframework/ide/vscode/commons/jandex/JdtLsIndexTest.java @@ -34,6 +34,7 @@ import org.springframework.ide.vscode.commons.java.IMethod; import org.springframework.ide.vscode.commons.java.IPrimitiveType; import org.springframework.ide.vscode.commons.java.IType; import org.springframework.ide.vscode.commons.jdtls.JdtLsIndex; +import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver; import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient; import org.springframework.ide.vscode.commons.protocol.java.TypeData; import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData; @@ -69,7 +70,7 @@ public class JdtLsIndexTest { } })); // Some valid URI necessary for URI#toString() to succeed - JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir"))); + JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir")), ProjectObserver.NULL); IType type = index.findType("java.util.Map"); assertNotNull(type); assertEquals("Ljava/util/Map;", type.getBindingKey()); @@ -91,7 +92,7 @@ public class JdtLsIndexTest { } })); // Some valid URI necessary for URI#toString() to succeed - JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir"))); + JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir")), ProjectObserver.NULL); IType type = index.findType("java.util.Map"); assertNotNull(type); @@ -114,7 +115,7 @@ public class JdtLsIndexTest { } })); // Some valid URI necessary for URI#toString() to succeed - JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir"))); + JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir")), ProjectObserver.NULL); IType type = index.findType("org.springframework.boot.autoconfigure.web.ServerProperties"); assertNotNull(type); @@ -138,7 +139,7 @@ public class JdtLsIndexTest { } })); // Some valid URI necessary for URI#toString() to succeed - JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir"))); + JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir")), ProjectObserver.NULL); List> results = index.fuzzySearchTypes("util.Map", true, false).collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())).block(); IType type = results.get(0).getT1(); assertEquals("io.netty.util.Mapping", type.getFullyQualifiedName()); @@ -155,7 +156,7 @@ public class JdtLsIndexTest { } })); // Some valid URI necessary for URI#toString() to succeed - JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir"))); + JdtLsIndex index = new JdtLsIndex(client, URI.create(System.getProperty("java.io.tmpdir")), ProjectObserver.NULL); List> results = index.fuzzySearchPackages("com.e", true, false).collectSortedList((o1, o2) -> o2.getT2().compareTo(o1.getT2())).block(); List packages = results.stream().map(t -> t.getT1()).collect(Collectors.toList()); assertEquals(Arrays.asList("com.example", "com.another.example"), packages); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java index b5e9c544e..67d02faeb 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/jdt/ls/JdtLsProjectCache.java @@ -207,7 +207,7 @@ public class JdtLsProjectCache implements InitializableJavaProjectsService { IJavaProject newProject = IS_JANDEX_INDEX ? new JavaProject(getFileObserver(), projectUri, classpath, JdtLsProjectCache.this) - : new JdtLsJavaProject(server.getClient(), projectUri, classpath); + : new JdtLsJavaProject(server.getClient(), projectUri, classpath, JdtLsProjectCache.this); IJavaProject oldProject = table.put(uri, newProject); if (oldProject != null) { notifyChanged(newProject);