diff --git a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml index 86af34284..61e615008 100644 --- a/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/plugin.xml @@ -358,4 +358,12 @@ --> + + + + 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 new file mode 100644 index 000000000..2bdddf0f6 --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandElement.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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.tooling.boot.ls.commands; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.lsp4e.LanguageServiceAccessor; +import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.ui.quickaccess.QuickAccessElement; + +/** + * @author Martin Lippert + */ +@SuppressWarnings("restriction") +public class LiveProcessCommandElement extends QuickAccessElement { + + public static final String COMMAND_LIST_PROCESSES = "sts/livedata/listProcesses"; + public static final String COMMAND_CONNECT = "sts/livedata/connect"; + public static final String COMMAND_REFRESH = "sts/livedata/refresh"; + public static final String COMMAND_DISCONNECT = "sts/livedata/disconnect"; + + private final String processKey; + private final String label; + private final String action; + + public LiveProcessCommandElement(String processKey, String label, String action) { + super(); + this.processKey = processKey; + this.label = label; + this.action = action; + } + + @Override + public String getLabel() { + if (COMMAND_REFRESH.equals(action)) { + return "Refresh Live Data for: " + label; + } + else if (COMMAND_CONNECT.equals(action)) { + return "Show live data for: " + label; + } + else if (COMMAND_DISCONNECT.equals(action)) { + return "Disconnect live data from: " + label; + } + else { + // error case + return "No live data action avaiable for: " + label; + } + } + + @Override + public ImageDescriptor getImageDescriptor() { + return null; + } + + @Override + public String getId() { + return processKey + action; + } + + @Override + public void execute() { + System.out.println("EXECUTE THE COMMAND !!!"); + + List<@NonNull LanguageServer> usedLanguageServers = LanguageServiceAccessor.getActiveLanguageServers(serverCapabilities -> true); + + if (usedLanguageServers.isEmpty()) { + return; + } + + ExecuteCommandParams commandParams = new ExecuteCommandParams(); + commandParams.setCommand(this.action); + + List arguments = new ArrayList<>(); + Map argumentMap = new HashMap<>(); + argumentMap.put("processKey", this.processKey); + arguments.add(argumentMap); + + commandParams.setArguments(arguments); + + CompletableFuture.allOf(usedLanguageServers.stream().map(ls -> + ls.getWorkspaceService().executeCommand(commandParams)).toArray(CompletableFuture[]::new)).join(); + } + +} 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 new file mode 100644 index 000000000..2bbecf7cf --- /dev/null +++ b/eclipse-language-servers/org.springframework.tooling.boot.ls/src/org/springframework/tooling/boot/ls/commands/LiveProcessCommandsQuickAccessProvider.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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.tooling.boot.ls.commands; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.lsp4e.LanguageServiceAccessor; +import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.ui.quickaccess.IQuickAccessComputer; +import org.eclipse.ui.quickaccess.IQuickAccessComputerExtension; +import org.eclipse.ui.quickaccess.QuickAccessElement; + +/** + * @author Martin Lippert + */ +@SuppressWarnings("restriction") +public class LiveProcessCommandsQuickAccessProvider implements IQuickAccessComputer, IQuickAccessComputerExtension { + + private List<@NonNull LanguageServer> usedLanguageServers; + + @Override + public QuickAccessElement[] computeElements() { + return new QuickAccessElement[0]; + } + + @Override + public void resetState() { + } + + @Override + public boolean needsRefresh() { + return this.usedLanguageServers == null; + } + + @Override + public QuickAccessElement[] computeElements(String query, IProgressMonitor monitor) { + System.out.println("COMPUTE QUICK ACCESS ELEMENTS !!!"); + + this.usedLanguageServers = LanguageServiceAccessor.getActiveLanguageServers(serverCapabilities -> true); + + if (usedLanguageServers.isEmpty()) { + return new QuickAccessElement[0]; + } + + ExecuteCommandParams commandParams = new ExecuteCommandParams(); + commandParams.setCommand(LiveProcessCommandElement.COMMAND_LIST_PROCESSES); + + List res = Collections.synchronizedList(new ArrayList<>()); + CompletableFuture.allOf(usedLanguageServers.stream().map(ls -> + 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()]); + } + + private void createCommandItems(List res, Object commandResult) { + System.out.println(commandResult); + + if (commandResult instanceof List) { + List allCommands = (List) commandResult; + for (Object command : allCommands) { + if (command instanceof Map) { + res.add(createCommandItem((Map)command)); + } + } + } + } + + private LiveProcessCommandElement createCommandItem(Map command) { + String processKey = (String) command.get("processKey"); + String label = (String) command.get("label"); + String action = (String) command.get("action"); + + return new LiveProcessCommandElement(processKey, label, action); + } + +} diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java index e642e8eae..587899852 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java +++ b/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/AbstractSpringBootApp.java @@ -43,6 +43,7 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ide.vscode.boot.java.livehover.v2.Boot1xRequestMapping; +import org.springframework.ide.vscode.boot.java.livehover.v2.ContextPath; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditionalParser; diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java index 92fb578ab..5030f0471 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/BootJavaLanguageServerComponents.java @@ -43,6 +43,7 @@ import org.springframework.ide.vscode.boot.java.links.SourceLinks; import org.springframework.ide.vscode.boot.java.livehover.ActiveProfilesProvider; import org.springframework.ide.vscode.boot.java.livehover.BeanInjectedIntoHoverProvider; import org.springframework.ide.vscode.boot.java.livehover.ComponentInjectionsHoverProvider; +import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessCommandHandler; import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnectorLocal; import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnectorRemote; import org.springframework.ide.vscode.boot.java.livehover.v2.SpringProcessConnectorService; @@ -163,6 +164,8 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent SpringProcessConnectorLocal liveHoverLocalProcessConnector = new SpringProcessConnectorLocal(liveDataConnector, liveDataProvider, projectObserver); liveProcessTracker = new SpringProcessTracker(liveHoverLocalProcessConnector, serverParams.watchDogInterval); + + new SpringProcessCommandHandler(server, liveDataConnector, liveHoverLocalProcessConnector); // liveHoverWatchdog = new SpringLiveHoverWatchdog(server, hoverProvider, runningAppProvider, // projectFinder, projectObserver, serverParams.watchDogInterval); diff --git a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/ContextPath.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveContextPathUtil.java similarity index 94% rename from headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/ContextPath.java rename to headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveContextPathUtil.java index 3759d5662..8fe348e94 100644 --- a/headless-services/commons/commons-boot-app-cli/src/main/java/org/springframework/ide/vscode/commons/boot/app/cli/ContextPath.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveContextPathUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2018 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 @@ -8,7 +8,7 @@ * Contributors: * Pivotal, Inc. - initial API and implementation *******************************************************************************/ -package org.springframework.ide.vscode.commons.boot.app.cli; +package org.springframework.ide.vscode.boot.java.livehover.v2; import java.util.Collection; @@ -20,9 +20,9 @@ import org.springframework.ide.vscode.commons.util.StringUtil; import com.google.common.collect.ImmutableList; -public class ContextPath { +public class LiveContextPathUtil { - protected static Logger logger = LoggerFactory.getLogger(ContextPath.class); + protected static Logger logger = LoggerFactory.getLogger(LiveContextPathUtil.class); public static final Collection BOOT_1X_CONTEXTPATH = ImmutableList.of("server.context-path", "server.contextPath", "SERVER_CONTEXT_PATH"); 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 new file mode 100644 index 000000000..25337ba29 --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/LiveProcessCommand.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * 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 class LiveProcessCommand { + + private String processKey; + private String label; + private String action; + + public String getProcessKey() { + return processKey; + } + + public void setProcessKey(String processKey) { + this.processKey = 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; + } + +} 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 cd3578f42..e245842d8 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 @@ -10,9 +10,142 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover.v2; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.ExecuteCommandParams; +import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + /** * @author mlippert */ public class SpringProcessCommandHandler { + + private static final String COMMAND_LIST_PROCESSES = "sts/livedata/listProcesses"; + private static final String COMMAND_CONNECT = "sts/livedata/connect"; + private static final String COMMAND_REFRESH = "sts/livedata/refresh"; + private static final String COMMAND_DISCONNECT = "sts/livedata/disconnect"; + + private final SpringProcessConnectorService connectorService; + private final SpringProcessConnectorLocal localProcessConnector; + + public SpringProcessCommandHandler(SimpleLanguageServer server, SpringProcessConnectorService connectorService, SpringProcessConnectorLocal localProcessConnector) { + this.connectorService = connectorService; + this.localProcessConnector = localProcessConnector; + + server.onCommand(COMMAND_LIST_PROCESSES, (params) -> { + return getProcessCommands(); + }); + + server.onCommand(COMMAND_CONNECT, (params) -> { + return connect(params); + }); + + server.onCommand(COMMAND_REFRESH, (params) -> { + return refresh(params); + }); + + server.onCommand(COMMAND_DISCONNECT, (params) -> { + return disconnect(params); + }); + + } + + private CompletableFuture connect(ExecuteCommandParams params) { + String processKey = getProcessKey(params); + if (processKey != null) { + SpringProcessDescriptor[] processes = localProcessConnector.getProcesses(); + for (SpringProcessDescriptor process : processes) { + if (process.getProcessKey().equals(processKey)) { + localProcessConnector.connectLocalProcess(process, false); + return CompletableFuture.completedFuture(null); + } + } + } + + return CompletableFuture.completedFuture(null); + } + + private CompletableFuture refresh(ExecuteCommandParams params) { + String processKey = getProcessKey(params); + if (processKey != null) { + connectorService.refreshProcess(processKey); + } + + return CompletableFuture.completedFuture(null); + } + + private CompletableFuture disconnect(ExecuteCommandParams params) { + return CompletableFuture.completedFuture(null); + } + + private CompletableFuture getProcessCommands() { + List result = new ArrayList<>(); + + Set alreadyConnected = new HashSet<>(); + + // already connected processes + SpringProcessConnector[] connectedProcesses = connectorService.getConnectedProcesses(); + 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); + + alreadyConnected.add(processKey); + } + + // other available local processes + SpringProcessDescriptor[] localProcesses = localProcessConnector.getProcesses(); + for (SpringProcessDescriptor localProcess : localProcesses) { + 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); + + result.add(command); + } + } + + return CompletableFuture.completedFuture((Object[]) result.toArray(new Object[result.size()])); + } + + private String getProcessKey(ExecuteCommandParams params) { + List arguments = params.getArguments(); + for (Object arg : arguments) { + if (arg instanceof Map) { + Object value = ((Map) arg).get("processKey"); + if (value != null) { + return value.toString(); + } + } + else if (arg instanceof JsonObject) { + JsonElement element = ((JsonObject) arg).get("processKey"); + if (element != null) { + return element.getAsString(); + } + } + } + + return null; + } } 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 8ac948151..7d8202ccb 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 @@ -15,6 +15,9 @@ package org.springframework.ide.vscode.boot.java.livehover.v2; */ public interface SpringProcessConnector { + String getProcessKey(); + String getLabel(); + void connect() throws Exception; void refresh() throws Exception; void disconnect() throws Exception; 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 8495ef230..dfd775eaf 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 @@ -10,6 +10,7 @@ *******************************************************************************/ package org.springframework.ide.vscode.boot.java.livehover.v2; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -61,7 +62,8 @@ public class SpringProcessConnectorLocal { private final Collection projects; - private final Set processes; + private final Set processes; + private final SpringProcessConnectorService processConnectorService; private final SpringProcessLiveDataProvider liveDataProvider; @@ -100,10 +102,28 @@ public class SpringProcessConnectorLocal { return false; } } + + 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 SpringProcessDescriptor[] updateProcesses() { List currentVms = VirtualMachine.list(); Set currentVMKeys = new HashSet<>(); + + List newProcesses = new ArrayList<>(); for (VirtualMachineDescriptor vm : currentVms) { @@ -114,8 +134,11 @@ public class SpringProcessConnectorLocal { currentVMKeys.add(processKey); - if (!processes.contains(processKey)) { - connectLocalProcess(processKey, vm, true); + SpringProcessDescriptor descriptor = new SpringProcessDescriptor(processKey, processID, processName, vm); + + if (!processes.contains(descriptor)) { + processes.add(descriptor); + newProcesses.add(descriptor); } } catch (Exception e) { @@ -123,20 +146,23 @@ public class SpringProcessConnectorLocal { } } - Iterator i = processes.iterator(); + Iterator i = processes.iterator(); while (i.hasNext()) { - String processKey = i.next(); + SpringProcessDescriptor processDescriptor = i.next(); + String processKey = processDescriptor.getProcessKey(); if (!currentVMKeys.contains(processKey)) { i.remove(); processConnectorService.disconnectProcess(processKey); } } + + return (SpringProcessDescriptor[]) newProcesses.toArray(new SpringProcessDescriptor[newProcesses.size()]); } - public void connectLocalProcess(String processKey, VirtualMachineDescriptor vmDescriptor, boolean checkAutoConnect) { - processes.add(processKey); - + public void connectLocalProcess(SpringProcessDescriptor descriptor, boolean checkAutoConnect) { VirtualMachine vm = null; + VirtualMachineDescriptor vmDescriptor = descriptor.getVm(); + try { String jmxAddress = null; vm = VirtualMachine.attach(vmDescriptor); @@ -165,9 +191,9 @@ public class SpringProcessConnectorLocal { String urlScheme = "http"; SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX( - liveDataProvider, processKey, jmxAddress, urlScheme, processID, processName, null, null); + liveDataProvider, descriptor.getProcessKey(), jmxAddress, urlScheme, processID, processName, null, null); - this.processConnectorService.connectProcess(processKey, connector); + this.processConnectorService.connectProcess(descriptor.getProcessKey(), connector); } } catch (Exception e) { @@ -186,34 +212,36 @@ public class SpringProcessConnectorLocal { } private boolean shouldAutoConnectToProcess(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 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.processes.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; - } + return false; - // default case: - 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; } private String getProcessID(VirtualMachineDescriptor descriptor) { 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 bf5a91a11..31912d6f3 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 @@ -46,6 +46,16 @@ public class SpringProcessConnectorOverJMX implements SpringProcessConnector { this.port = port; } + @Override + public String getProcessKey() { + return processKey; + } + + @Override + public String getLabel() { + return processID + " (" + processName + ") "; + } + @Override public void connect() throws Exception { } diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java index c05314f92..cc0e8716d 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessConnectorService.java @@ -30,16 +30,20 @@ public class SpringProcessConnectorService { private final ScheduledThreadPoolExecutor scheduler; private final ConcurrentMap connectors; + private final ConcurrentMap connectedSuccess; public SpringProcessConnectorService() { this.scheduler = new ScheduledThreadPoolExecutor(10); this.connectors = new ConcurrentHashMap<>(); + this.connectedSuccess = new ConcurrentHashMap<>(); } public void connectProcess(String processKey, SpringProcessConnector connector) { log.info("connect to process: " + processKey); this.connectors.put(processKey, connector); + this.connectedSuccess.put(processKey, false); + try { scheduleConnect(processKey, connector, 0, TimeUnit.SECONDS, 0); } @@ -61,6 +65,7 @@ public class SpringProcessConnectorService { log.info("disconnect from process: " + processKey); SpringProcessConnector connector = this.connectors.remove(processKey); + this.connectedSuccess.put(processKey, false); if (connector != null) { scheduleDisconnect(processKey, connector, 0, TimeUnit.SECONDS, 0); @@ -68,7 +73,8 @@ public class SpringProcessConnectorService { } public SpringProcessConnector[] getConnectedProcesses() { - return (SpringProcessConnector[]) this.connectors.values().toArray(new SpringProcessConnector[this.connectors.values().size()]); + return (SpringProcessConnector[]) this.connectors.values().stream() + .filter((connector) -> connectedSuccess.get(connector.getProcessKey())).toArray(SpringProcessConnector[]::new); } /** @@ -119,6 +125,7 @@ public class SpringProcessConnectorService { this.scheduler.schedule(() -> { try { connector.refresh(); + this.connectedSuccess.put(processKey, true); } catch (Exception e) { log.info("problem occured during process live data refresh", e); 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 new file mode 100644 index 000000000..f3adfa92e --- /dev/null +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessDescriptor.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * 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; + +import com.sun.tools.attach.VirtualMachineDescriptor; + +/** + * @author Martin Lippert + */ +@SuppressWarnings("restriction") +public class SpringProcessDescriptor { + + private final String processKey; + private final String processID; + private final String processName; + private final VirtualMachineDescriptor vm; + + public SpringProcessDescriptor(String processKey, String processID, String processName, VirtualMachineDescriptor vm) { + this.processKey = processKey; + this.processID = processID; + this.processName = processName; + this.vm = vm; + } + + public String getProcessKey() { + return this.processKey; + } + + public String getProcessID() { + return processID; + } + + public String getProcessName() { + return processName; + } + + public VirtualMachineDescriptor getVm() { + return vm; + } + + public String getLabel() { + return processID + " (" + processName + ") "; + } + + @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; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + 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/SpringProcessLiveDataExtractorOverJMX.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataExtractorOverJMX.java index 7ded93e9e..1874ab7b8 100644 --- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataExtractorOverJMX.java +++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/livehover/v2/SpringProcessLiveDataExtractorOverJMX.java @@ -33,7 +33,6 @@ import org.json.JSONArray; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.ide.vscode.commons.boot.app.cli.ContextPath; import org.springframework.ide.vscode.commons.util.StringUtil; import com.google.common.collect.ImmutableList; @@ -442,7 +441,7 @@ public class SpringProcessLiveDataExtractorOverJMX { if (result != null) { bootVersion = "2.x"; } - return bootVersion != null && environment != null ? ContextPath.getContextPath(bootVersion, environment) : null; + return bootVersion != null && environment != null ? LiveContextPathUtil.getContextPath(bootVersion, environment) : null; } catch (IOException e) { //Ignore... happens a low when app is stopped } catch (ExecutionException e) { diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java index d656aa291..49996be3f 100644 --- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java +++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/project/harness/MockRunningAppProvider.java @@ -21,12 +21,12 @@ import java.util.Properties; import org.mockito.Mockito; import org.springframework.ide.vscode.boot.java.handlers.RunningAppProvider; +import org.springframework.ide.vscode.boot.java.livehover.v2.LiveContextPathUtil; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveBeansModel; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveConditional; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveProperties; import org.springframework.ide.vscode.boot.java.livehover.v2.LivePropertiesJsonParser; import org.springframework.ide.vscode.boot.java.livehover.v2.LiveRequestMapping; -import org.springframework.ide.vscode.commons.boot.app.cli.ContextPath; import org.springframework.ide.vscode.commons.boot.app.cli.LocalSpringBootApp; import org.springframework.ide.vscode.commons.boot.app.cli.SpringBootApp; import org.springframework.ide.vscode.commons.util.ExceptionUtil; @@ -100,7 +100,7 @@ public class MockRunningAppProvider { } public MockAppBuilder contextPathEnvJson(String bootVersion, String envJson) throws Exception { - String contextPath = ContextPath.getContextPath(bootVersion, envJson); + String contextPath = LiveContextPathUtil.getContextPath(bootVersion, envJson); when(app.getContextPath()).thenReturn(contextPath); return this; }