Ensure fallbacks kick in JDT LS
When either JDT LS is too old, or not available... then disbabled the JdtLsProjectCache so that maven / gradle will be used instead.
This commit is contained in:
@@ -660,7 +660,7 @@ public class SimpleLanguageServer implements Sts4LanguageServer, LanguageClientA
|
||||
return this.async;
|
||||
}
|
||||
|
||||
public synchronized Disposable addClasspathListener(ClasspathListener classpathListener) {
|
||||
public synchronized Disposable addClasspathListener(ClasspathListener classpathListener) throws Exception {
|
||||
if (classpathListenerManager == null) {
|
||||
classpathListenerManager = new ClasspathListenerManager(this);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.core.internal.runtime.Log;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
@@ -29,6 +28,17 @@ import org.springframework.tooling.jdt.ls.extension.ClasspathListenerManager.Cla
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class ClasspathListenerHandler implements IDelegateCommandHandler {
|
||||
|
||||
static final boolean isSupported = checkSupported();
|
||||
private static boolean checkSupported() {
|
||||
try {
|
||||
JavaClientConnection.class.getMethod("executeClientCommand", String.class, Object[].class);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Logger.log(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static class Subscribptions {
|
||||
|
||||
@@ -101,6 +111,9 @@ public class ClasspathListenerHandler implements IDelegateCommandHandler {
|
||||
|
||||
@Override
|
||||
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor monitor) throws Exception {
|
||||
if (!isSupported) {
|
||||
throw new UnsupportedOperationException("Command '"+commandId+"' not supported on older versions of JDT Language Server");
|
||||
}
|
||||
log("ClasspathListenerHandler executeCommand " + commandId + ", " + arguments);
|
||||
switch (commandId) {
|
||||
case "sts.java.addClasspathListener":
|
||||
|
||||
@@ -13,11 +13,13 @@ package org.springframework.ide.vscode.boot;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
|
||||
import org.springframework.ide.vscode.boot.jdt.ls.JdtLsProjectCache;
|
||||
import org.springframework.ide.vscode.boot.jdt.ls.JavaProjectsService;
|
||||
import org.springframework.ide.vscode.boot.metadata.DefaultSpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.types.TypeUtil;
|
||||
@@ -31,6 +33,7 @@ import org.springframework.ide.vscode.commons.languageserver.java.CompositeJavaP
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.CompositeProjectOvserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
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.languageserver.util.LSFactory;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.maven.MavenCore;
|
||||
@@ -80,32 +83,51 @@ public class BootLanguageServerParams {
|
||||
public static LSFactory<BootLanguageServerParams> createDefault() {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
JdtLsProjectCache jdtProjectCache = new JdtLsProjectCache(server);
|
||||
javaProjectFinder.addJavaProjectFinder(jdtProjectCache);
|
||||
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(jdtProjectCache, mavenProjectCache, gradleProjectCache));
|
||||
|
||||
DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(javaProjectFinder, projectObserver);
|
||||
JdtLsProjectCache jdtProjectCache = new JdtLsProjectCache(server, createFallbackProjectCache(server));
|
||||
DefaultSpringPropertyIndexProvider indexProvider = new DefaultSpringPropertyIndexProvider(jdtProjectCache, jdtProjectCache);
|
||||
indexProvider.setProgressService(server.getProgressService());
|
||||
|
||||
return new BootLanguageServerParams(
|
||||
javaProjectFinder.filter(BootProjectUtil::isBootProject),
|
||||
projectObserver,
|
||||
jdtProjectCache.filter(BootProjectUtil::isBootProject),
|
||||
jdtProjectCache,
|
||||
indexProvider,
|
||||
(IDocument doc) -> new TypeUtil(javaProjectFinder.find(new TextDocumentIdentifier(doc.getUri()))),
|
||||
(IDocument doc) -> new TypeUtil(jdtProjectCache.find(new TextDocumentIdentifier(doc.getUri()))),
|
||||
RunningAppProvider.DEFAULT,
|
||||
SpringLiveHoverWatchdog.DEFAULT_INTERVAL
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
private static JavaProjectsService createFallbackProjectCache(SimpleLanguageServer server) {
|
||||
CompositeJavaProjectFinder javaProjectFinder = new CompositeJavaProjectFinder();
|
||||
|
||||
MavenProjectCache mavenProjectCache = new MavenProjectCache(server, MavenCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new MavenProjectFinder(mavenProjectCache));
|
||||
|
||||
GradleProjectCache gradleProjectCache = new GradleProjectCache(server, GradleCore.getDefault(), true, Paths.get(IJavaProject.PROJECT_CACHE_FOLDER));
|
||||
javaProjectFinder.addJavaProjectFinder(new GradleProjectFinder(gradleProjectCache));
|
||||
|
||||
CompositeProjectOvserver projectObserver = new CompositeProjectOvserver(Arrays.asList(mavenProjectCache, gradleProjectCache));
|
||||
|
||||
return new JavaProjectsService() {
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
projectObserver.removeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
projectObserver.addListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IJavaProject> find(TextDocumentIdentifier doc) {
|
||||
return javaProjectFinder.find(doc);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static LSFactory<BootLanguageServerParams> createTestDefault(SpringPropertyIndexProvider indexProvider, TypeUtilProvider typeUtilProvider) {
|
||||
return (SimpleLanguageServer server) -> {
|
||||
// Initialize project finders, project caches and project observers
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Pivotal, 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
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*
|
||||
* Contributors:
|
||||
* Pivotal, Inc. - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.jdt.ls;
|
||||
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
|
||||
public interface JavaProjectsService extends JavaProjectFinder, ProjectObserver {
|
||||
}
|
||||
@@ -30,66 +30,105 @@ import org.springframework.ide.vscode.commons.java.ClasspathData;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IJavadocProvider;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath;
|
||||
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.ClasspathListener;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
import org.springframework.ide.vscode.commons.util.Assert;
|
||||
import org.springframework.ide.vscode.commons.util.CollectorUtil;
|
||||
import org.springframework.ide.vscode.commons.util.ExceptionUtil;
|
||||
import org.springframework.ide.vscode.commons.util.UriUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import reactor.core.Disposable;
|
||||
|
||||
public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
|
||||
|
||||
public class JdtLsProjectCache implements JavaProjectsService {
|
||||
|
||||
private CompletableFuture<Void> initialized = new CompletableFuture<Void>();
|
||||
|
||||
private SimpleLanguageServer server;
|
||||
private Map<String, JdtLsProject> table = new HashMap<String, JdtLsProject>();
|
||||
private Logger log = LoggerFactory.getLogger(JdtLsProjectCache.class);
|
||||
private List<Listener> listeners = new ArrayList<>();
|
||||
|
||||
public JdtLsProjectCache(SimpleLanguageServer server) {
|
||||
private final JavaProjectsService fallback;
|
||||
|
||||
public JdtLsProjectCache(SimpleLanguageServer server, JavaProjectsService fallback) {
|
||||
Assert.isNotNull(fallback);
|
||||
this.fallback = fallback;
|
||||
this.server = server;
|
||||
CompletableFuture<Disposable> disposable = new CompletableFuture<Disposable>();
|
||||
this.server.onInitialized(() ->
|
||||
disposable.complete(server.addClasspathListener(new ClasspathListener() {
|
||||
@Override
|
||||
public void changed(Event event) {
|
||||
synchronized (table) {
|
||||
String uri = UriUtil.normalize(event.projectUri);
|
||||
if (event.deleted) {
|
||||
JdtLsProject deleted = table.remove(uri);
|
||||
notifyDelete(deleted);
|
||||
} else {
|
||||
JdtLsProject newProject = new JdtLsProject(event.name, uri, event.classpath);
|
||||
JdtLsProject oldProject = table.put(uri, newProject);
|
||||
if (oldProject != null) {
|
||||
notifyChanged(newProject);
|
||||
this.server.onInitialized(() -> {
|
||||
try {
|
||||
disposable.complete(server.addClasspathListener(new ClasspathListener() {
|
||||
@Override
|
||||
public void changed(Event event) {
|
||||
synchronized (table) {
|
||||
String uri = UriUtil.normalize(event.projectUri);
|
||||
if (event.deleted) {
|
||||
JdtLsProject deleted = table.remove(uri);
|
||||
notifyDelete(deleted);
|
||||
} else {
|
||||
notifyCreated(newProject);
|
||||
JdtLsProject newProject = new JdtLsProject(event.name, uri, event.classpath);
|
||||
JdtLsProject oldProject = table.put(uri, newProject);
|
||||
if (oldProject != null) {
|
||||
notifyChanged(newProject);
|
||||
} else {
|
||||
notifyCreated(newProject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
initialized.complete(null);
|
||||
} catch (Throwable e) {
|
||||
if (isNoJdtError(e)) {
|
||||
log.info("JDT Language Server not available. Fallback classpath provider will be used instead.");
|
||||
} else if (isOldJdt(e)) {
|
||||
log.info("JDT Lanuage Server too old. Fallback classpath provider will be used instead.");
|
||||
} else {
|
||||
log.error("Unexpected error registering classpath listener with JDT. Fallback classpath provider will be used instead.", e);
|
||||
}
|
||||
}))
|
||||
);
|
||||
disposable.complete(()-> {});
|
||||
initialized.completeExceptionally(e);
|
||||
}
|
||||
});
|
||||
this.server.onShutdown(() ->
|
||||
disposable.thenAccept(Disposable::dispose).join()
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isOldJdt(Throwable e) {
|
||||
return ExceptionUtil.getMessage(e).contains("'sts.java.addClasspathListener' not supported");
|
||||
}
|
||||
|
||||
private boolean isNoJdtError(Throwable e) {
|
||||
return ExceptionUtil.getMessage(e).contains("command 'java.execute.workspaceCommand' not found");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(Listener listener) {
|
||||
synchronized (listeners) {
|
||||
listeners.add(listener);
|
||||
Assert.isLegal(initialized.isDone()); //Adding / removing listeners prior to inialization isn't supported. (If this is really needed, it can be
|
||||
// bit its a bit trickier to implement that correctly)
|
||||
if (initialized.isCompletedExceptionally()) {
|
||||
fallback.addListener(listener);
|
||||
} else {
|
||||
synchronized (listeners) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeListener(Listener listener) {
|
||||
synchronized (listeners) {
|
||||
listeners.remove(listener);
|
||||
Assert.isLegal(initialized.isDone()); //Adding / removing listeners prior to inialization isn't supported. (If this is really needed, it can be
|
||||
// bit its a bit trickier to implement that correctly)
|
||||
if (initialized.isCompletedExceptionally()) {
|
||||
fallback.removeListener(listener);
|
||||
} else {
|
||||
synchronized (listeners) {
|
||||
listeners.remove(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,17 +169,21 @@ public class JdtLsProjectCache implements JavaProjectFinder, ProjectObserver {
|
||||
|
||||
@Override
|
||||
public Optional<IJavaProject> find(TextDocumentIdentifier doc) {
|
||||
String uri = UriUtil.normalize(doc.getUri());
|
||||
|
||||
synchronized (table) {
|
||||
for (Entry<String, JdtLsProject> e : table.entrySet()) {
|
||||
String projectUri = e.getKey();
|
||||
if (UriUtil.contains(projectUri, uri) ) {
|
||||
return Optional.of(e.getValue());
|
||||
}
|
||||
if (initialized.isDone()) {
|
||||
if (initialized.isCompletedExceptionally()) {
|
||||
return fallback.find(doc);
|
||||
}
|
||||
String uri = UriUtil.normalize(doc.getUri());
|
||||
|
||||
synchronized (table) {
|
||||
for (Entry<String, JdtLsProject> e : table.entrySet()) {
|
||||
String projectUri = e.getKey();
|
||||
if (UriUtil.contains(projectUri, uri) ) {
|
||||
return Optional.of(e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,18 @@ cd ${HOME}/git/eclipse.jdt.ls
|
||||
jdt_ls_repo=$(pwd)
|
||||
./mvnw -Pserver-distro -Pupdate-site clean package
|
||||
|
||||
# Section below disabled for now. So we can work on the 'fallbacks' with missing or old vscode-java
|
||||
# Build vscode-java
|
||||
cd ${HOME}/git/vscode-java
|
||||
rm -fr server
|
||||
mkdir server
|
||||
tar -xvzf ${jdt_ls_repo}/org.eclipse.jdt.ls.product/distro/jdt-language-server-*.tar.gz -C ./server
|
||||
npm install
|
||||
rm -fr *.vsix
|
||||
npm install -D vsce
|
||||
./node_modules/.bin/vsce package
|
||||
code --uninstall-extension redhat.java || echo "Not installed redhat.java"
|
||||
code --install-extension *.vsix
|
||||
#cd ${HOME}/git/vscode-java
|
||||
#rm -fr server
|
||||
#mkdir server
|
||||
#tar -xvzf ${jdt_ls_repo}/org.eclipse.jdt.ls.product/distro/jdt-language-server-*.tar.gz -C ./server
|
||||
#npm install
|
||||
#rm -fr *.vsix
|
||||
#npm install -D vsce
|
||||
#./node_modules/.bin/vsce package
|
||||
#code --uninstall-extension redhat.java || echo "Not installed redhat.java"
|
||||
#code --install-extension *.vsix
|
||||
|
||||
# Build spring boot ls
|
||||
cd $workdir
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
'use strict';
|
||||
// The module 'vscode' contains the VS Code extensibility API
|
||||
// Import the module and reference it with the alias vscode in your code below
|
||||
|
||||
import * as VSCode from 'vscode';
|
||||
import * as Path from 'path';
|
||||
|
||||
Reference in New Issue
Block a user