diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProcessCommand.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProcessCommand.java index a91eb9748..7ce412038 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProcessCommand.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProcessCommand.java @@ -18,33 +18,34 @@ public class LiveProcessCommand { private String processKey; private String label; private String action; + private String projectName; - public String getProcessKey() { - return processKey; + public LiveProcessCommand(String action, String processKey, String label, String projectName) { + super(); + this.processKey = processKey; + this.label = label; + this.action = action; + this.projectName = projectName; } - public void setProcessKey(String processKey) { - this.processKey = processKey; + public String getProcessKey() { + return processKey; } public String getLabel() { return label; } - public void setLabel(String label) { - this.label = label; - } - public String getAction() { return action; } - public void setAction(String action) { - this.action = action; - } - @Override public String toString() { return "LiveProcessCommand [processKey=" + processKey + ", label=" + label + ", action=" + action + "]"; } + + public String getProjectName() { + return projectName; + } } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessCommandHandler.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessCommandHandler.java index 1e38ca27b..6fa2c9838 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessCommandHandler.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessCommandHandler.java @@ -126,19 +126,8 @@ public class SpringProcessCommandHandler { for (SpringProcessConnector process : connectedProcesses) { String processKey = process.getProcessKey(); String label = process.getLabel(); - - LiveProcessCommand refreshCommand = new LiveProcessCommand(); - refreshCommand.setAction(COMMAND_REFRESH); - refreshCommand.setLabel(label); - refreshCommand.setProcessKey(processKey); - result.add(refreshCommand); - - LiveProcessCommand disconnectCommand = new LiveProcessCommand(); - disconnectCommand.setAction(COMMAND_DISCONNECT); - disconnectCommand.setLabel(label); - disconnectCommand.setProcessKey(processKey); - result.add(disconnectCommand); - + result.add(new LiveProcessCommand(COMMAND_REFRESH, processKey, label, process.getProjectName())); + result.add(new LiveProcessCommand(COMMAND_DISCONNECT, processKey, label, process.getProjectName())); alreadyConnected.add(processKey); } @@ -149,13 +138,8 @@ public class SpringProcessCommandHandler { String processKey = localProcess.getProcessKey(); if (!alreadyConnected.contains(processKey)) { String label = localProcess.getLabel(); - String action = COMMAND_CONNECT; - LiveProcessCommand command = new LiveProcessCommand(); - command.setAction(action); - command.setLabel(label); - command.setProcessKey(processKey); - + LiveProcessCommand command = new LiveProcessCommand(COMMAND_CONNECT, processKey, label, localProcess.getProjectName()); result.add(command); } } @@ -167,14 +151,7 @@ public class SpringProcessCommandHandler { String processKey = SpringProcessConnectorRemote.getProcessKey(remoteProcess); if (!alreadyConnected.contains(processKey)) { String label = "remote process: " + remoteProcess.getJmxurl(); - String action = COMMAND_CONNECT; - - LiveProcessCommand command = new LiveProcessCommand(); - command.setAction(action); - command.setLabel(label); - command.setProcessKey(processKey); - - result.add(command); + result.add(new LiveProcessCommand(COMMAND_CONNECT, processKey, label, null)); } } log.info("getProcessCommands => {}", result); diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnector.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnector.java index 3806a658d..0d33060e8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnector.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnector.java @@ -24,5 +24,6 @@ public interface SpringProcessConnector { void addConnectorChangeListener(SpringProcessConnectionChangeListener listener); void removeConnectorChangeListener(SpringProcessConnectionChangeListener listener); + String getProjectName(); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java index d040d5927..99c60f5f2 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorLocal.java @@ -41,28 +41,6 @@ public class SpringProcessConnectorLocal { private static final String LOCAL_CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress"; - /** - * this property is automatically set my language servers to their own processes - * to avoid the spring boot language server to connect to itself or another language server process - * - * this is primarily an optimization to avoid that overhead of trying to connect to such a process - */ - private static final String LANGUAGE_SERVER_PROPERTY = "sts4.languageserver.name"; - - /** - * this property can be set to a running spring boot app at startup to indicatew that the - * process of this running boot app belongs to a specific project - * - * in that case, the running process is only connected if the workspace contains that project - */ - private static final String SPRING_APP_PROJECT_NAME_PROPERTY = "spring.boot.project.name"; - - /** - * common prefix for the vm descriptor display name of Eclipse processes, we can filter that out - * of the list of processes to connect to immediately to avoid further processing - */ - private static final String ECLIPSE_PROCESS_DISPLAY_NAME_PREFIX = "org.eclipse.equinox.launcher.Main"; - private final Collection projects; private final Set processes; @@ -180,10 +158,7 @@ public class SpringProcessConnectorLocal { List> futures = new ArrayList<>(); for (SpringProcessDescriptor process : processes) { - CompletableFuture checkStatusFuture = checkStatus(process); - CompletableFuture result = checkStatusFuture.thenAccept((status) -> process.setStatus(status)); - - futures.add(result); + futures.add(process.updateStatus(projects::contains)); } CompletableFuture allStatusUpdates = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); @@ -196,39 +171,6 @@ public class SpringProcessConnectorLocal { } } - private CompletableFuture checkStatus(SpringProcessDescriptor descriptor) { - return CompletableFuture.supplyAsync(() -> { - - VirtualMachine vm = null; - try { - vm = VirtualMachine.attach(descriptor.getVm()); - - if (shouldIgnore(descriptor.getVm(), vm)) { - return SpringProcessStatus.IGNORE; - } - - if (shouldAutoConnect(descriptor.getVm(), vm)) { - return SpringProcessStatus.AUTO_CONNECT; - } - - return SpringProcessStatus.REGULAR; - } - catch (Exception e) { - return SpringProcessStatus.IGNORE; - } - finally { - if (vm != null) { - try { - vm.detach(); - } - catch (Exception e) { - log.error("error detaching from vm: " + descriptor.getVm().id(), e); - } - } - } - }); - } - public void connectProcess(SpringProcessDescriptor descriptor) { VirtualMachine vm = null; VirtualMachineDescriptor vmDescriptor = descriptor.getVm(); @@ -257,7 +199,7 @@ public class SpringProcessConnectorLocal { String urlScheme = "http"; SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX( - descriptor.getProcessKey(), jmxAddress, urlScheme, processID, processName, null, null); + descriptor.getProcessKey(), jmxAddress, urlScheme, processID, processName, descriptor.getProjectName(), null, null); this.processConnectorService.connectProcess(descriptor.getProcessKey(), connector); } @@ -281,47 +223,6 @@ public class SpringProcessConnectorLocal { return this.processConnectorService.isConnected(processKey); } - private boolean shouldIgnore(VirtualMachineDescriptor vmDescriptor, VirtualMachine vm) { - try { - String displayName = vmDescriptor.displayName(); - if (displayName != null && displayName.startsWith(ECLIPSE_PROCESS_DISPLAY_NAME_PREFIX)) { - log.info("Eclipse process found, do not connect: " + vmDescriptor.id()); - return true; - } - - Properties systemProperties = vm.getSystemProperties(); - - Object languageServerIndicatorProperty = systemProperties.get(LANGUAGE_SERVER_PROPERTY); - if (languageServerIndicatorProperty != null) { - log.info("language server process found, do not connect: " + vmDescriptor.id()); - return true; - } - - } - catch (Exception e) { - return true; - } - - return false; - } - - private boolean shouldAutoConnect(VirtualMachineDescriptor vmDescriptor, VirtualMachine vm) { - try { - Properties systemProperties = vm.getSystemProperties(); - - Object projectNameProperty = systemProperties.get(SPRING_APP_PROJECT_NAME_PROPERTY); - if (projectNameProperty instanceof String) { - log.info("Spring boot process found: " + projectNameProperty); - return this.projects.contains((String) projectNameProperty); - } - } - catch (Exception e) { - return false; - } - - return false; - } - private String getProcessID(VirtualMachineDescriptor descriptor) { return descriptor.id(); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorOverJMX.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorOverJMX.java index a90e63933..7a8a84d53 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorOverJMX.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorOverJMX.java @@ -39,6 +39,8 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { private final String jmxURL; private final String urlScheme; private final String port; + final private String projectName; + // not final, might be updated with data from JMX process, if not initially set private String processID; @@ -51,9 +53,9 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { private JMXServiceURL jmxServiceURL; private final NotificationListener notificationListener; - + public SpringProcessConnectorOverJMX(String processKey, String jmxURL, - String urlScheme, String processID, String processName, String host, String port) { + String urlScheme, String processID, String processName, String projectName, String host, String port) { this.processKey = processKey; @@ -61,6 +63,7 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { this.urlScheme = urlScheme; this.processID = processID; this.processName = processName; + this.projectName = projectName; this.host = host; this.port = port; @@ -176,4 +179,9 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { } } + @Override + public String getProjectName() { + return projectName; + } + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorRemote.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorRemote.java index db926ea61..5d6a60c6d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorRemote.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorRemote.java @@ -199,7 +199,7 @@ public class SpringProcessConnectorRemote { String urlScheme = remoteProcess.getUrlScheme(); // boolean keepChecking = _appData.isKeepChecking(); - SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX(processKey, jmxURL, urlScheme, processID, processName, host, port); + SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX(processKey, jmxURL, urlScheme, processID, processName, null, host, port); processConnectorService.connectProcess(processKey, connector); } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java index c15bc38f8..8f3ed174d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java @@ -10,20 +10,52 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover.v2; +import java.util.Properties; +import java.util.concurrent.CompletableFuture; +import java.util.function.Predicate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor; /** * @author Martin Lippert */ -@SuppressWarnings("restriction") public class SpringProcessDescriptor { + private static final Logger log = LoggerFactory.getLogger(SpringProcessDescriptor.class); + + /** + * this property is automatically set my language servers to their own processes + * to avoid the spring boot language server to connect to itself or another language server process + * + * this is primarily an optimization to avoid that overhead of trying to connect to such a process + */ + private static final String LANGUAGE_SERVER_PROPERTY = "sts4.languageserver.name"; + + /** + * this property can be set to a running spring boot app at startup to indicatew that the + * process of this running boot app belongs to a specific project + * + * in that case, the running process is only connected if the workspace contains that project + */ + private static final String SPRING_APP_PROJECT_NAME_PROPERTY = "spring.boot.project.name"; + + /** + * common prefix for the vm descriptor display name of Eclipse processes, we can filter that out + * of the list of processes to connect to immediately to avoid further processing + */ + private static final String ECLIPSE_PROCESS_DISPLAY_NAME_PREFIX = "org.eclipse.equinox.launcher.Main"; + private final String processKey; private final String processID; private final String processName; private final VirtualMachineDescriptor vm; private SpringProcessStatus status; + private String projectName; public SpringProcessDescriptor(String processKey, String processID, String processName, VirtualMachineDescriptor vm) { this.processKey = processKey; @@ -53,11 +85,7 @@ public class SpringProcessDescriptor { public String getLabel() { return processID + " (" + processName + ")"; } - - public void setStatus(SpringProcessStatus status) { - this.status = status; - } - + public SpringProcessStatus getStatus() { return this.status; } @@ -83,8 +111,86 @@ public class SpringProcessDescriptor { return false; return true; } - - + public CompletableFuture updateStatus(Predicate projectIsKnown) { + return CompletableFuture.supplyAsync(() -> { + this.status = checkStatus(projectIsKnown); + return null; + }); + } + + private SpringProcessStatus checkStatus(Predicate projectIsKnown) { + VirtualMachine vm = null; + try { + vm = VirtualMachine.attach(this.getVm()); + if (shouldIgnore(this.getVm(), vm)) { + return SpringProcessStatus.IGNORE; + } + if (shouldAutoConnect(this.getVm(), vm, projectIsKnown)) { + return SpringProcessStatus.AUTO_CONNECT; + } + + return SpringProcessStatus.REGULAR; + } + catch (Exception e) { + return SpringProcessStatus.IGNORE; + } + finally { + if (vm != null) { + try { + vm.detach(); + } + catch (Exception e) { + log.error("error detaching from vm: " + this.getVm().id(), e); + } + } + } + } + + private boolean shouldIgnore(VirtualMachineDescriptor vmDescriptor, VirtualMachine vm) { + try { + String displayName = vmDescriptor.displayName(); + if (displayName != null && displayName.startsWith(ECLIPSE_PROCESS_DISPLAY_NAME_PREFIX)) { + log.info("Eclipse process found, do not connect: " + vmDescriptor.id()); + return true; + } + + Properties systemProperties = vm.getSystemProperties(); + + Object languageServerIndicatorProperty = systemProperties.get(LANGUAGE_SERVER_PROPERTY); + if (languageServerIndicatorProperty != null) { + log.info("language server process found, do not connect: " + vmDescriptor.id()); + return true; + } + + } + catch (Exception e) { + return true; + } + + return false; + } + + private boolean shouldAutoConnect(VirtualMachineDescriptor vmDescriptor, VirtualMachine vm, Predicate projectIsKnown) { + try { + Properties systemProperties = vm.getSystemProperties(); + + Object projectName = systemProperties.get(SPRING_APP_PROJECT_NAME_PROPERTY); + if (projectName instanceof String) { + log.info("Spring boot process found: " + projectName); + this.projectName = (String) projectName; + return projectIsKnown.test((String) projectName); + } + } + catch (Exception e) { + return false; + } + + return false; + } + + public String getProjectName() { + return projectName; + } }