Preliminary implementation of boot-dash menus...
for live process connection management
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -24,5 +24,6 @@ public interface SpringProcessConnector {
|
||||
|
||||
void addConnectorChangeListener(SpringProcessConnectionChangeListener listener);
|
||||
void removeConnectorChangeListener(SpringProcessConnectionChangeListener listener);
|
||||
String getProjectName();
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String> projects;
|
||||
private final Set<SpringProcessDescriptor> processes;
|
||||
@@ -180,10 +158,7 @@ public class SpringProcessConnectorLocal {
|
||||
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
|
||||
for (SpringProcessDescriptor process : processes) {
|
||||
CompletableFuture<SpringProcessStatus> checkStatusFuture = checkStatus(process);
|
||||
CompletableFuture<Void> result = checkStatusFuture.thenAccept((status) -> process.setStatus(status));
|
||||
|
||||
futures.add(result);
|
||||
futures.add(process.updateStatus(projects::contains));
|
||||
}
|
||||
|
||||
CompletableFuture<Void> allStatusUpdates = CompletableFuture.allOf((CompletableFuture[]) futures.toArray(new CompletableFuture[futures.size()]));
|
||||
@@ -196,39 +171,6 @@ public class SpringProcessConnectorLocal {
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<SpringProcessStatus> 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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Void> updateStatus(Predicate<String> projectIsKnown) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
this.status = checkStatus(projectIsKnown);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private SpringProcessStatus checkStatus(Predicate<String> 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<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user