first steps towards detecting changes in live running apps across, at the moment across internal restarts via devtools
This commit is contained in:
@@ -148,12 +148,25 @@ public class SpringBootApp {
|
||||
}
|
||||
|
||||
private boolean isSpringBootAppClasspath() throws IOException {
|
||||
Properties props = this.vm.getSystemProperties();
|
||||
String classpath = (String) props.get("java.class.path");
|
||||
String[] cpElements = getClasspath(classpath);
|
||||
return contains(cpElements, "spring-boot");
|
||||
return contains(getClasspath(), "spring-boot");
|
||||
}
|
||||
|
||||
public String[] getClasspath() throws IOException {
|
||||
Properties props = this.vm.getSystemProperties();
|
||||
String classpath = (String) props.get("java.class.path");
|
||||
String[] cpElements = splitClasspath(classpath);
|
||||
return cpElements;
|
||||
}
|
||||
|
||||
public String getJavaCommand() throws IOException {
|
||||
Properties props = this.vm.getSystemProperties();
|
||||
if (props.contains("sun.java.command")) {
|
||||
return props.getProperty("sun.java.command");
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsSystemProperty(Object key) throws IOException {
|
||||
Properties props = this.vm.getSystemProperties();
|
||||
@@ -340,7 +353,7 @@ public class SpringBootApp {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected String[] getClasspath(String classpath) {
|
||||
protected String[] splitClasspath(String classpath) {
|
||||
List<String> classpathElements = new ArrayList<>();
|
||||
if (classpath != null) {
|
||||
StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
|
||||
|
||||
@@ -11,14 +11,19 @@
|
||||
package org.springframework.ide.vscode.commons.boot.app.cli.livebean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.ide.vscode.commons.util.Log;
|
||||
import org.springframework.ide.vscode.commons.util.StringUtil;
|
||||
|
||||
import com.google.common.collect.ImmutableCollection;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
@@ -210,4 +215,8 @@ public class LiveBeansModel {
|
||||
return beansViaName.isEmpty(); //Assumes every bean has a name.
|
||||
}
|
||||
|
||||
public Set<String> getBeanNames() {
|
||||
return beansViaName.keySet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017 Pivotal, Inc.
|
||||
* Copyright (c) 2017, 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
|
||||
@@ -28,6 +28,11 @@ public class BootJavaConfig {
|
||||
return enabled == null || enabled.booleanValue();
|
||||
}
|
||||
|
||||
public boolean isChangeDetectionEnabled() {
|
||||
Boolean enabled = settings.getBoolean("boot-java", "change-detection", "on");
|
||||
return enabled == null || enabled.booleanValue();
|
||||
}
|
||||
|
||||
public void handleConfigurationChange(Settings newConfig) {
|
||||
Log.info("Settings received: "+newConfig);
|
||||
this.settings = newConfig;
|
||||
|
||||
@@ -56,6 +56,7 @@ import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetContext;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringIndexer;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringLiveChangeDetectionWatchdog;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SpringLiveHoverWatchdog;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueHoverProvider;
|
||||
@@ -95,6 +96,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
private final SpringIndexer indexer;
|
||||
private final SpringPropertyIndexProvider propertyIndexProvider;
|
||||
private final SpringLiveHoverWatchdog liveHoverWatchdog;
|
||||
private final SpringLiveChangeDetectionWatchdog liveChangeDetectionWatchdog;
|
||||
private final ProjectObserver projectObserver;
|
||||
private final BootJavaConfig config;
|
||||
private final CompilationUnitCache cuCache;
|
||||
@@ -169,6 +171,8 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
// }
|
||||
});
|
||||
|
||||
liveChangeDetectionWatchdog = new SpringLiveChangeDetectionWatchdog(this, server, serverParams.projectObserver, serverParams.runningAppProvider, projectFinder, serverParams.watchDogInterval);
|
||||
|
||||
codeLensHandler = createCodeLensEngine();
|
||||
documents.onCodeLens(codeLensHandler);
|
||||
|
||||
@@ -177,11 +181,21 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
|
||||
workspaceService.onDidChangeConfiguraton(settings -> {
|
||||
config.handleConfigurationChange(settings);
|
||||
|
||||
// live hover watchdog
|
||||
if (config.isBootHintsEnabled()) {
|
||||
liveHoverWatchdog.enableHighlights();
|
||||
} else {
|
||||
liveHoverWatchdog.disableHighlights();
|
||||
}
|
||||
|
||||
// live change detection watchdog
|
||||
if (config.isChangeDetectionEnabled()) {
|
||||
liveChangeDetectionWatchdog.enableHighlights();
|
||||
}
|
||||
else {
|
||||
liveChangeDetectionWatchdog.disableHighlights();
|
||||
}
|
||||
});
|
||||
|
||||
server.onInitialize(this::initialize);
|
||||
@@ -213,6 +227,8 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
|
||||
private void initialized() {
|
||||
this.indexer.serverInitialized();
|
||||
this.liveChangeDetectionWatchdog.start();
|
||||
|
||||
// TODO: due to a missing message from lsp4e this "initialized" is not called in
|
||||
// the LSP4E case
|
||||
// if this gets fixed, the code should move here (from "initialize" above)
|
||||
@@ -223,6 +239,7 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
|
||||
private void shutdown() {
|
||||
this.liveHoverWatchdog.shutdown();
|
||||
this.liveChangeDetectionWatchdog.shutdown();
|
||||
this.indexer.shutdown();
|
||||
this.cuCache.dispose();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*******************************************************************************
|
||||
* 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.java.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class Change {
|
||||
|
||||
private List<LiveBean> newBeans;
|
||||
private List<LiveBean> deletedBeans;
|
||||
|
||||
public Change() {
|
||||
}
|
||||
|
||||
public List<LiveBean> getNewBeans() {
|
||||
return newBeans;
|
||||
}
|
||||
|
||||
public List<LiveBean> getDeletedBeans() {
|
||||
return deletedBeans;
|
||||
}
|
||||
|
||||
public void addDeletedBean(LiveBean bean) {
|
||||
if (deletedBeans == null) {
|
||||
deletedBeans = new ArrayList<>();
|
||||
}
|
||||
|
||||
deletedBeans.add(bean);
|
||||
}
|
||||
|
||||
public void addNewBean(LiveBean bean) {
|
||||
if (newBeans == null) {
|
||||
newBeans = new ArrayList<>();
|
||||
}
|
||||
|
||||
newBeans.add(bean);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*******************************************************************************
|
||||
* 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.java.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ChangeDetectionHistory {
|
||||
|
||||
private Map<String, ChangeHistory> changeHistory;
|
||||
|
||||
public ChangeDetectionHistory() {
|
||||
this.changeHistory = new HashMap<>();
|
||||
}
|
||||
|
||||
public Change checkForChanges(SpringBootApp app) {
|
||||
String virtualID = getVirtualAppID(app);
|
||||
|
||||
if (!changeHistory.containsKey(virtualID)) {
|
||||
ChangeHistory appHistory = new ChangeHistory(virtualID);
|
||||
changeHistory.put(virtualID, appHistory);
|
||||
}
|
||||
|
||||
ChangeHistory appHistory = changeHistory.get(virtualID);
|
||||
appHistory.updateProcess(app);
|
||||
|
||||
Change result = appHistory.checkForUpdates();
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getVirtualAppID(SpringBootApp app) {
|
||||
// TODO: this needs a lot more work to make this a real VIRTUAL ID which detects the same app
|
||||
// running across different app restarts, so the process ID is not the best way to do this (just
|
||||
// a temporary solution)
|
||||
return app.getProcessID();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*******************************************************************************
|
||||
* 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.java.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBeansModel;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class ChangeHistory {
|
||||
|
||||
private static final List<LiveBean> EMPTY_BEANS_LIST = new ArrayList<>(0);
|
||||
|
||||
private final String virtualAppID;
|
||||
|
||||
private SpringBootApp runningProcess;
|
||||
private LiveBeansModel lastBeans;
|
||||
|
||||
public ChangeHistory(String virtualAppID) {
|
||||
this.virtualAppID = virtualAppID;
|
||||
}
|
||||
|
||||
public void updateProcess(SpringBootApp app) {
|
||||
this.runningProcess = app;
|
||||
}
|
||||
|
||||
public Change checkForUpdates() {
|
||||
Change result = null;
|
||||
|
||||
LiveBeansModel currentBeans = this.runningProcess.getBeans();
|
||||
|
||||
if (lastBeans == null) {
|
||||
lastBeans = currentBeans;
|
||||
}
|
||||
else if (currentBeans != null) {
|
||||
result = calculateBeansDiff(lastBeans, currentBeans, result);
|
||||
lastBeans = currentBeans;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Change calculateBeansDiff(LiveBeansModel previous, LiveBeansModel current, Change result) {
|
||||
if (previous == current) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Set<String> currentNames = current.getBeanNames();
|
||||
Set<String> previousNames = previous.getBeanNames();
|
||||
|
||||
Set<String> allNames = new HashSet<>(currentNames);
|
||||
allNames.addAll(previousNames);
|
||||
|
||||
for (String name : allNames) {
|
||||
List<LiveBean> currentBeans = current.getBeansOfName(name);
|
||||
List<LiveBean> previousBeans = previous.getBeansOfName(name);
|
||||
|
||||
result = calculateBeansDiff(previousBeans, currentBeans, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Change calculateBeansDiff(List<LiveBean> previousBeans, List<LiveBean> currentBeans, Change result) {
|
||||
if (currentBeans == null) currentBeans = EMPTY_BEANS_LIST;
|
||||
if (previousBeans == null) previousBeans = EMPTY_BEANS_LIST;
|
||||
|
||||
for (LiveBean bean : previousBeans) {
|
||||
if (!contains(currentBeans, bean)) {
|
||||
|
||||
if (result == null) {
|
||||
result = new Change();
|
||||
}
|
||||
|
||||
result.addDeletedBean(bean);
|
||||
}
|
||||
}
|
||||
|
||||
for (LiveBean bean : currentBeans) {
|
||||
if (!contains(previousBeans, bean)) {
|
||||
|
||||
if (result == null) {
|
||||
result = new Change();
|
||||
}
|
||||
|
||||
result.addNewBean(bean);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean contains(List<LiveBean> beans, LiveBean bean) {
|
||||
for (LiveBean beansFromList : beans) {
|
||||
if (StringUtils.equals(beansFromList.getId(), bean.getId())
|
||||
&& StringUtils.equals(beansFromList.getType(), bean.getType())
|
||||
&& StringUtils.equals(beansFromList.getResource(), bean.getResource())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*******************************************************************************
|
||||
* 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.java.utils;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.lsp4j.Diagnostic;
|
||||
import org.eclipse.lsp4j.DiagnosticSeverity;
|
||||
import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.PublishDiagnosticsParams;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.BootJavaLanguageServerComponents;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.links.VSCodeSourceLinks;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp;
|
||||
import org.springframework.ide.vscode.commons.boot.app.cli.livebean.LiveBean;
|
||||
import org.springframework.ide.vscode.commons.java.IClasspath;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
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.jdt.ls.Classpath;
|
||||
import org.springframework.ide.vscode.commons.languageserver.jdt.ls.Classpath.CPE;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
public class SpringLiveChangeDetectionWatchdog {
|
||||
|
||||
public static final Duration DEFAULT_INTERVAL = Duration.ofMillis(5000);
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(SpringLiveChangeDetectionWatchdog.class);
|
||||
|
||||
private final long POLLING_INTERVAL_MILLISECONDS;
|
||||
|
||||
private final SimpleLanguageServer server;
|
||||
private final RunningAppProvider runningAppProvider;
|
||||
private final SourceLinks sourceLinks;
|
||||
|
||||
private final ChangeDetectionHistory changeHistory;
|
||||
private final Set<IJavaProject> observedProjects;
|
||||
|
||||
private boolean changeDetectionEnabled = true;
|
||||
private Timer timer;
|
||||
|
||||
public SpringLiveChangeDetectionWatchdog(
|
||||
BootJavaLanguageServerComponents bootJavaLanguageServerComponents,
|
||||
SimpleLanguageServer server,
|
||||
ProjectObserver projectObserver,
|
||||
RunningAppProvider runningAppProvider,
|
||||
JavaProjectFinder projectFinder,
|
||||
Duration pollingInterval
|
||||
) {
|
||||
this.observedProjects = new HashSet<>();
|
||||
|
||||
this.server = server;
|
||||
this.runningAppProvider = runningAppProvider;
|
||||
|
||||
this.POLLING_INTERVAL_MILLISECONDS = pollingInterval == null ? DEFAULT_INTERVAL.toMillis() : pollingInterval.toMillis();
|
||||
|
||||
this.changeHistory = new ChangeDetectionHistory();
|
||||
this.sourceLinks = new VSCodeSourceLinks(bootJavaLanguageServerComponents);
|
||||
|
||||
if (projectObserver != null) {
|
||||
projectObserver.addListener(new Listener() {
|
||||
|
||||
@Override
|
||||
public void deleted(IJavaProject project) {
|
||||
observedProjects.remove(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(IJavaProject project) {
|
||||
observedProjects.add(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changed(IJavaProject project) {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
if (changeDetectionEnabled && timer == null) {
|
||||
logger.debug("Starting SpringLiveChangeDetectionWatchdog");
|
||||
this.timer = new Timer();
|
||||
|
||||
TimerTask task = new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
update();
|
||||
}
|
||||
};
|
||||
|
||||
timer.schedule(task, 0, POLLING_INTERVAL_MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void shutdown() {
|
||||
if (timer != null) {
|
||||
logger.info("Shutting down SpringLiveChangeDetectionWatchdog");
|
||||
timer.cancel();
|
||||
timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (changeDetectionEnabled) {
|
||||
try {
|
||||
SpringBootApp[] runningBootApps = runningAppProvider.getAllRunningSpringApps().toArray(new SpringBootApp[0]);
|
||||
for (SpringBootApp app : runningBootApps) {
|
||||
updateApp(app);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateApp(SpringBootApp app) {
|
||||
Change change = changeHistory.checkForChanges(app);
|
||||
if (change != null) {
|
||||
publishDetectedChange(change, app);
|
||||
}
|
||||
}
|
||||
|
||||
private void publishDetectedChange(Change change, SpringBootApp app) {
|
||||
Map<String, List<Diagnostic>> diagnostics = new HashMap<>();
|
||||
|
||||
IJavaProject[] projects = findProjectsFor(app);
|
||||
|
||||
List<LiveBean> deletedBeans = change.getDeletedBeans();
|
||||
if (deletedBeans != null) {
|
||||
for (LiveBean liveBean : deletedBeans) {
|
||||
Diagnostic diag = new Diagnostic();
|
||||
diag.setSeverity(DiagnosticSeverity.Information);
|
||||
diag.setRange(new Range(new Position(0, 0), new Position(0, 0)));
|
||||
diag.setSource("Spring Boot Change Detection Mechanism");
|
||||
|
||||
diag.setMessage("bean removed from app: " + liveBean.getId());
|
||||
|
||||
String docURI = getDocURI(liveBean, projects);
|
||||
if (docURI != null) {
|
||||
List<Diagnostic> diags = diagnostics.computeIfAbsent(docURI, (s) -> new ArrayList<>());
|
||||
diags.add(diag);
|
||||
}
|
||||
else {
|
||||
logger.info("deleted bean could not be associated with a doc URI: " + liveBean.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<LiveBean> newBeans = change.getNewBeans();
|
||||
if (newBeans != null) {
|
||||
for (LiveBean liveBean : newBeans) {
|
||||
Diagnostic diag = new Diagnostic();
|
||||
diag.setSeverity(DiagnosticSeverity.Information);
|
||||
diag.setRange(new Range(new Position(0, 0), new Position(0, 0)));
|
||||
diag.setSource("Spring Boot Change Detection Mechanism");
|
||||
|
||||
diag.setMessage("new bean detected: " + liveBean.getId());
|
||||
|
||||
String docURI = getDocURI(liveBean, projects);
|
||||
if (docURI != null) {
|
||||
List<Diagnostic> diags = diagnostics.computeIfAbsent(docURI, (s) -> new ArrayList<>());
|
||||
diags.add(diag);
|
||||
}
|
||||
else {
|
||||
logger.info("new bean could not be associated with a doc URI: " + liveBean.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (String docURI : diagnostics.keySet()) {
|
||||
PublishDiagnosticsParams params = new PublishDiagnosticsParams(docURI, diagnostics.get(docURI));
|
||||
server.getClient().publishDiagnostics(params);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private IJavaProject[] findProjectsFor(SpringBootApp app) {
|
||||
List<IJavaProject> result = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Set<String> runningClasspath = new HashSet<>();
|
||||
Collections.addAll(runningClasspath, app.getClasspath());
|
||||
|
||||
for (IJavaProject project : this.observedProjects) {
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Collection<CPE> entries = classpath.getClasspathEntries();
|
||||
for (CPE cpe : entries) {
|
||||
if (Classpath.ENTRY_KIND_SOURCE.equals(cpe.getKind())) {
|
||||
String path = cpe.getOutputFolder();
|
||||
if (runningClasspath.contains(path)) {
|
||||
result.add(project);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
logger.error("find projects failed with: ", e);
|
||||
}
|
||||
|
||||
return (IJavaProject[]) result.toArray(new IJavaProject[result.size()]);
|
||||
}
|
||||
|
||||
private String getDocURI(LiveBean liveBean, IJavaProject[] projects) {
|
||||
String result = null;
|
||||
|
||||
String resource = liveBean.getResource();
|
||||
Pattern BRACKETS = Pattern.compile("\\[[^\\]]*\\]");
|
||||
|
||||
Matcher matcher = BRACKETS.matcher(resource);
|
||||
if (matcher.find()) {
|
||||
String type = resource.substring(0, matcher.start()).trim();
|
||||
String path = resource.substring(matcher.start()+1, matcher.end()-1);
|
||||
|
||||
for (IJavaProject project : projects) {
|
||||
if (SpringResource.FILE.equals(type)) {
|
||||
String relativePath = SpringResource.projectRelativePath(project, path);
|
||||
|
||||
if (relativePath != path && path.endsWith(SourceLinks.CLASS)) {
|
||||
result = sourceLinks.sourceLinkUrlForClasspathResource(project, relativePath).get();
|
||||
break;
|
||||
} else {
|
||||
result = sourceLinks.sourceLinkForResourcePath(Paths.get(path)).get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (SpringResource.CLASS_PATH_RESOURCE.equals(type)) {
|
||||
result = sourceLinks.sourceLinkUrlForClasspathResource(project, path).get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result != null) {
|
||||
int position = result.lastIndexOf('#');
|
||||
if (position > 0) {
|
||||
result = result.substring(0, position);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public synchronized void enableHighlights() {
|
||||
if (!changeDetectionEnabled) {
|
||||
changeDetectionEnabled = true;
|
||||
refreshEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void disableHighlights() {
|
||||
if (changeDetectionEnabled) {
|
||||
changeDetectionEnabled = false;
|
||||
refreshEnablement();
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshEnablement() {
|
||||
if (changeDetectionEnabled) {
|
||||
start();
|
||||
} else {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -37,7 +37,6 @@ import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
*/
|
||||
public class SpringLiveHoverWatchdog {
|
||||
|
||||
|
||||
public static final Duration DEFAULT_INTERVAL = Duration.ofMillis(5000);
|
||||
|
||||
Logger logger = LoggerFactory.getLogger(SpringLiveHoverWatchdog.class);
|
||||
|
||||
@@ -29,8 +29,8 @@ import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
*/
|
||||
public class SpringResource {
|
||||
|
||||
private static final String FILE = "file";
|
||||
private static final String CLASS_PATH_RESOURCE = "class path resource";
|
||||
public static final String FILE = "file";
|
||||
public static final String CLASS_PATH_RESOURCE = "class path resource";
|
||||
|
||||
private SourceLinks sourceLinks;
|
||||
private String type;
|
||||
@@ -65,7 +65,7 @@ public class SpringResource {
|
||||
Optional<String> linkUrl;
|
||||
switch (type) {
|
||||
case FILE:
|
||||
String relativePath = projectRelativePath(path);
|
||||
String relativePath = projectRelativePath(project, path);
|
||||
if (relativePath != path && path.endsWith(SourceLinks.CLASS)) {
|
||||
linkUrl = sourceLinks.sourceLinkUrlForClasspathResource(project, relativePath);
|
||||
} else {
|
||||
@@ -73,7 +73,7 @@ public class SpringResource {
|
||||
}
|
||||
// not a project relative path
|
||||
return linkUrl.isPresent() ? Renderables.link(relativePath, linkUrl.get()).toMarkdown()
|
||||
: "`" + projectRelativePath(path) + "`";
|
||||
: "`" + projectRelativePath(project, path) + "`";
|
||||
case CLASS_PATH_RESOURCE:
|
||||
linkUrl = sourceLinks.sourceLinkUrlForClasspathResource(project, path);
|
||||
return linkUrl.isPresent() ? Renderables.link(path, linkUrl.get()).toMarkdown() : "`"+path+"`";
|
||||
@@ -82,7 +82,7 @@ public class SpringResource {
|
||||
}
|
||||
}
|
||||
|
||||
private String projectRelativePath(String pathStr) {
|
||||
public static String projectRelativePath(IJavaProject project, String pathStr) {
|
||||
Path path = Paths.get(pathStr);
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Iterable<File> ofs = () -> IClasspathUtil.getOutputFolders(classpath).iterator();
|
||||
|
||||
Reference in New Issue
Block a user