diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandElement.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandElement.java index 7444217b2..71f3ed125 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandElement.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandElement.java @@ -78,8 +78,6 @@ public class LiveProcessCommandElement extends QuickAccessElement { @Override public void execute() { - System.out.println("EXECUTE THE COMMAND !!!"); - List<@NonNull LanguageServer> usedLanguageServers = LanguageServiceAccessor.getActiveLanguageServers(serverCapabilities -> true); if (usedLanguageServers.isEmpty()) { diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandsQuickAccessProvider.java b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandsQuickAccessProvider.java index 2bbecf7cf..51e319a62 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandsQuickAccessProvider.java +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandsQuickAccessProvider.java @@ -49,8 +49,6 @@ public class LiveProcessCommandsQuickAccessProvider implements IQuickAccessCompu @Override public QuickAccessElement[] computeElements(String query, IProgressMonitor monitor) { - System.out.println("COMPUTE QUICK ACCESS ELEMENTS !!!"); - this.usedLanguageServers = LanguageServiceAccessor.getActiveLanguageServers(serverCapabilities -> true); if (usedLanguageServers.isEmpty()) { @@ -65,12 +63,6 @@ public class LiveProcessCommandsQuickAccessProvider implements IQuickAccessCompu ls.getWorkspaceService().executeCommand(commandParams).thenAcceptAsync(commandResult -> createCommandItems(res, commandResult))).toArray(CompletableFuture[]::new)).join(); - System.out.println("quick access elements computed: " + res.size()); - - for (QuickAccessElement quickAccessElement : res) { - System.out.println("generated element: " + quickAccessElement.getLabel()); - } - return res.toArray(new QuickAccessElement[res.size()]); } 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 e245842d8..90e89d5f5 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 @@ -61,10 +61,10 @@ public class SpringProcessCommandHandler { private CompletableFuture connect(ExecuteCommandParams params) { String processKey = getProcessKey(params); if (processKey != null) { - SpringProcessDescriptor[] processes = localProcessConnector.getProcesses(); + SpringProcessDescriptor[] processes = localProcessConnector.getProcesses(false, SpringProcessStatus.REGULAR); for (SpringProcessDescriptor process : processes) { if (process.getProcessKey().equals(processKey)) { - localProcessConnector.connectLocalProcess(process, false); + localProcessConnector.connectProcess(process); return CompletableFuture.completedFuture(null); } } @@ -83,6 +83,11 @@ public class SpringProcessCommandHandler { } private CompletableFuture disconnect(ExecuteCommandParams params) { + String processKey = getProcessKey(params); + if (processKey != null) { + connectorService.disconnectProcess(processKey); + } + return CompletableFuture.completedFuture(null); } @@ -96,20 +101,26 @@ public class SpringProcessCommandHandler { for (SpringProcessConnector process : connectedProcesses) { String processKey = process.getProcessKey(); String label = process.getLabel(); - String action = COMMAND_REFRESH; - LiveProcessCommand command = new LiveProcessCommand(); - command.setAction(action); - command.setLabel(label); - command.setProcessKey(processKey); - - result.add(command); + LiveProcessCommand refreshCommand = new LiveProcessCommand(); + refreshCommand.setAction(COMMAND_REFRESH); + refreshCommand.setLabel(label); + refreshCommand.setProcessKey(processKey); + result.add(refreshCommand); + if (localProcessConnector.isLocalProcess(process.getProcessKey())) { + LiveProcessCommand disconnectCommand = new LiveProcessCommand(); + disconnectCommand.setAction(COMMAND_DISCONNECT); + disconnectCommand.setLabel(label); + disconnectCommand.setProcessKey(processKey); + result.add(disconnectCommand); + } + alreadyConnected.add(processKey); } // other available local processes - SpringProcessDescriptor[] localProcesses = localProcessConnector.getProcesses(); + SpringProcessDescriptor[] localProcesses = localProcessConnector.getProcesses(true, SpringProcessStatus.REGULAR); for (SpringProcessDescriptor localProcess : localProcesses) { String processKey = localProcess.getProcessKey(); if (!alreadyConnected.contains(processKey)) { diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectionChangeListener.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectionChangeListener.java new file mode 100644 index 000000000..641f1de0e --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectionChangeListener.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2019 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.livehover.v2; + +/** + * @author Martin Lippert + */ +public interface SpringProcessConnectionChangeListener { + + void connectionClosed(String processKey); + +} 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 7d8202ccb..2d4007eb3 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 @@ -21,5 +21,8 @@ public interface SpringProcessConnector { void connect() throws Exception; void refresh() throws Exception; void disconnect() throws Exception; + + void addConnectorChangeListener(SpringProcessConnectionChangeListener listener); + void removeConnectorChangeListener(SpringProcessConnectionChangeListener listener); } 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 dfd775eaf..0c0f898ad 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 @@ -18,6 +18,8 @@ import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,6 +73,7 @@ public class SpringProcessConnectorLocal { ProjectObserver projectObserver) { this.projects = Collections.synchronizedCollection(new HashSet<>()); this.processes = Collections.synchronizedSet(new HashSet<>()); + this.liveDataProvider = liveDataProvider; this.processConnectorService = processConnector; @@ -103,22 +106,26 @@ public class SpringProcessConnectorLocal { } } - public SpringProcessDescriptor[] getProcesses() { - if (processes.size() == 0) { - updateProcesses(); - } - - return (SpringProcessDescriptor[]) processes.toArray(new SpringProcessDescriptor[processes.size()]); - } - - public void searchForNewProcesses() { - SpringProcessDescriptor[] newProcesses = updateProcesses(); - - for (SpringProcessDescriptor descriptor : newProcesses) { - connectLocalProcess(descriptor, true); - } + public boolean isLocalProcess(String processKey) { + return this.processes.stream().anyMatch(process -> processKey.equals(process.getProcessKey())); } + public SpringProcessDescriptor[] getProcesses(boolean update, SpringProcessStatus status) { + if (update) { + SpringProcessDescriptor[] newProcesses = updateProcesses(); + if (newProcesses.length > 0) { + updateStatus(newProcesses); + } + } + + if (status != null) { + return processes.stream().filter((process) -> status.equals(process.getStatus())).toArray(SpringProcessDescriptor[]::new); + } + else { + return (SpringProcessDescriptor[]) processes.toArray(new SpringProcessDescriptor[processes.size()]); + } + } + public SpringProcessDescriptor[] updateProcesses() { List currentVms = VirtualMachine.list(); Set currentVMKeys = new HashSet<>(); @@ -159,7 +166,66 @@ public class SpringProcessConnectorLocal { return (SpringProcessDescriptor[]) newProcesses.toArray(new SpringProcessDescriptor[newProcesses.size()]); } - public void connectLocalProcess(SpringProcessDescriptor descriptor, boolean checkAutoConnect) { + private void updateStatus(SpringProcessDescriptor[] newProcesses) { + List> futures = new ArrayList<>(); + + for (SpringProcessDescriptor newProcess : newProcesses) { + CompletableFuture checkStatusFuture = checkStatus(newProcess); + CompletableFuture result = checkStatusFuture.thenAccept((status) -> newProcess.setStatus(status)); + + futures.add(result); + } + + CompletableFuture allStatusUpdates = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()])); + try { + allStatusUpdates.get(3, TimeUnit.SECONDS); + } + catch (Exception e) { + log.info("timeout or problem occured while updating the status of the new processes"); + } + } + + private CompletableFuture checkStatus(SpringProcessDescriptor descriptor) { + if (SpringProcessStatus.UNKNOWN.equals(descriptor.getStatus())) { + return CompletableFuture.supplyAsync(() -> { + + VirtualMachine vm = null; + try { + vm = VirtualMachine.attach(descriptor.getVm()); + + boolean ignore = shouldIgnore(descriptor.getVm(), vm); + boolean autoConnect = shouldAutoConnect(descriptor.getVm(), vm); + + if (ignore) { + return SpringProcessStatus.IGNORE; + } + else if (autoConnect) { + return SpringProcessStatus.AUTO_CONNECT; + } + else { + 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); + } + } + } + }); + } + + return CompletableFuture.completedFuture(SpringProcessStatus.UNKNOWN); + } + + public void connectProcess(SpringProcessDescriptor descriptor) { VirtualMachine vm = null; VirtualMachineDescriptor vmDescriptor = descriptor.getVm(); @@ -167,10 +233,6 @@ public class SpringProcessConnectorLocal { String jmxAddress = null; vm = VirtualMachine.attach(vmDescriptor); - if (checkAutoConnect && !shouldAutoConnectToProcess(vmDescriptor, vm)) { - return; - } - try { jmxAddress = vm.getAgentProperties().getProperty(LOCAL_CONNECTOR_ADDRESS); } catch (Exception e) { @@ -211,37 +273,45 @@ public class SpringProcessConnectorLocal { } } - private boolean shouldAutoConnectToProcess(VirtualMachineDescriptor vmDescriptor, VirtualMachine vm) { - return false; + 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; + } -// 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 false; -// } -// -// 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); -// } -// -// Object languageServerIndicatorProperty = systemProperties.get(LANGUAGE_SERVER_PROPERTY); -// if (languageServerIndicatorProperty != null) { -// log.info("language server process found, do not connect: " + vmDescriptor.id()); -// return false; -// } -// -// } -// catch (Exception e) { -// return false; -// } -// -// // default case: -// 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) { @@ -253,5 +323,5 @@ public class SpringProcessConnectorLocal { int firstSpace = rawName.indexOf(' '); return firstSpace < 0 ? rawName : rawName.substring(0, firstSpace); } - + } 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 31912d6f3..bcfe83aec 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 @@ -10,6 +10,9 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover.v2; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXServiceURL; @@ -33,6 +36,8 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { private final String host; private final String port; + private final List listeners; + public SpringProcessConnectorOverJMX(SpringProcessLiveDataProvider liveDataProvider, String processKey, String jmxURL, String urlScheme, String processID, String processName, String host, String port) { @@ -44,6 +49,8 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { this.processName = processName; this.host = host; this.port = port; + + this.listeners = new CopyOnWriteArrayList<>(); } @Override @@ -103,4 +110,22 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { this.liveDataProvider.remove(processKey); } + @Override + public void addConnectorChangeListener(SpringProcessConnectionChangeListener listener) { + this.listeners.add(listener); + } + + @Override + public void removeConnectorChangeListener(SpringProcessConnectionChangeListener listener) { + this.listeners.remove(listener); + } + + private void announceConnectionClosed() { + for (SpringProcessConnectionChangeListener listener : this.listeners) { + listener.connectionClosed(processKey); + } + } + + + } 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 f3adfa92e..c15bc38f8 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 @@ -23,11 +23,15 @@ public class SpringProcessDescriptor { private final String processName; private final VirtualMachineDescriptor vm; + private SpringProcessStatus status; + public SpringProcessDescriptor(String processKey, String processID, String processName, VirtualMachineDescriptor vm) { this.processKey = processKey; this.processID = processID; this.processName = processName; this.vm = vm; + + this.status = SpringProcessStatus.UNKNOWN; } public String getProcessKey() { @@ -47,17 +51,20 @@ public class SpringProcessDescriptor { } public String getLabel() { - return processID + " (" + processName + ") "; + return processID + " (" + processName + ")"; + } + + public void setStatus(SpringProcessStatus status) { + this.status = status; + } + + public SpringProcessStatus getStatus() { + return this.status; } @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((processID == null) ? 0 : processID.hashCode()); - result = prime * result + ((processKey == null) ? 0 : processKey.hashCode()); - result = prime * result + ((processName == null) ? 0 : processName.hashCode()); - return result; + return processKey.hashCode(); } @Override @@ -69,22 +76,15 @@ public class SpringProcessDescriptor { if (getClass() != obj.getClass()) return false; SpringProcessDescriptor other = (SpringProcessDescriptor) obj; - if (processID == null) { - if (other.processID != null) - return false; - } else if (!processID.equals(other.processID)) - return false; if (processKey == null) { if (other.processKey != null) return false; } else if (!processKey.equals(other.processKey)) return false; - if (processName == null) { - if (other.processName != null) - return false; - } else if (!processName.equals(other.processName)) - return false; return true; } + + + } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessStatus.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessStatus.java new file mode 100644 index 000000000..aa30c86c6 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessStatus.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2019 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 + * https://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Pivotal, Inc. - initial API and implementation + *******************************************************************************/ +package org.springframework.ide.vscode.boot.java.livehover.v2; + +public enum SpringProcessStatus { + + UNKNOWN, IGNORE, REGULAR, AUTO_CONNECT + +} diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessTracker.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessTracker.java index ac6770882..a641acf86 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessTracker.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessTracker.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2017, 2019 Pivotal, Inc. + * Copyright (c) 2019 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 @@ -74,13 +74,19 @@ public class SpringProcessTracker { } private void update() { - log.info("scan for local processes cycle..."); - try { - this.localProcessConnector.searchForNewProcesses(); - } - catch (Throwable e) { - log.error("error searching for local processes", e); - } +// log.info("scan for local processes cycle..."); +// try { +// SpringProcessDescriptor[] autoConnectProcesses = this.localProcessConnector.getProcesses(true, SpringProcessStatus.AUTO_CONNECT); +// +// for (SpringProcessDescriptor process : autoConnectProcesses) { +// log.info("auto-connect to process: " + process.getProcessKey()); +// +// this.localProcessConnector.connectProcess(process); +// } +// } +// catch (Throwable e) { +// log.error("error searching for local processes", e); +// } } }