Prefer connecting via JMX to process. New UI for show/refresh/hide
This commit is contained in:
@@ -96,6 +96,11 @@ public class BootJavaConfig implements InitializingBean {
|
||||
return enabled != null && enabled.booleanValue();
|
||||
}
|
||||
|
||||
public boolean isShowingAllJvmProcesses() {
|
||||
Boolean isAll = settings.getBoolean("boot-java", "live-information", "all-local-java-processes");
|
||||
return isAll != null && isAll.booleanValue();
|
||||
}
|
||||
|
||||
public String[] xmlBeansFoldersToScan() {
|
||||
String foldersStr = settings.getString("boot-java", "support-spring-xml-config", "scan-folders");
|
||||
if (foldersStr != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018, 2023 Pivotal, Inc.
|
||||
* Copyright (c) 2018, 2024 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
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.eclipse.lsp4j.CodeActionKind;
|
||||
import org.eclipse.lsp4j.CodeActionOptions;
|
||||
@@ -200,6 +201,33 @@ public class BootLanguageServerBootApp {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
SpringProcessConnectorRemote localAppsFromCommandsConnector(SimpleLanguageServer server, SpringProcessConnectorService liveDataService) {
|
||||
SpringProcessConnectorRemote bean = new SpringProcessConnectorRemote(server, liveDataService);
|
||||
final Map<String, RemoteBootAppData> localApps = new HashMap<>();
|
||||
final Gson gson = new Gson();
|
||||
server.onCommand("sts/livedata/localAdd", params -> {
|
||||
synchronized(localApps) {
|
||||
RemoteBootAppData[] newAdditions = params.getArguments().stream().map(a -> gson.fromJson((JsonElement) a, RemoteBootAppData.class)).toArray(RemoteBootAppData[]::new);
|
||||
for (RemoteBootAppData app : newAdditions) {
|
||||
localApps.put(app.getJmxurl(), app);
|
||||
}
|
||||
bean.updateApps(localApps.values().toArray(new RemoteBootAppData[localApps.size()]));
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
});
|
||||
server.onCommand("sts/livedata/localRemove", params -> {
|
||||
synchronized(localApps) {
|
||||
List<RemoteBootAppData> removedApps = params.getArguments().stream().map(o -> o instanceof JsonElement ? ((JsonElement) o).getAsString() : (String) o).map(localApps::remove).collect(Collectors.toList());
|
||||
if (!removedApps.isEmpty()) {
|
||||
bean.updateApps(localApps.values().toArray(new RemoteBootAppData[localApps.size()]));
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
});
|
||||
return bean;
|
||||
}
|
||||
|
||||
@ConditionalOnMissingClass("org.springframework.ide.vscode.languageserver.testharness.LanguageServerHarness")
|
||||
@Bean AdHocSpringPropertyIndexProvider adHocProperties(BootLanguageServerParams params, FileObserver fileObserver, DocumentEventListenerManager documentEvents) {
|
||||
return new AdHocSpringPropertyIndexProvider(params.projectFinder, params.projectObserver, fileObserver, documentEvents);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016, 2023 Pivotal, Inc.
|
||||
* Copyright (c) 2016, 2024 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
|
||||
@@ -143,14 +143,15 @@ public class BootJavaLanguageServerComponents implements LanguageServerComponent
|
||||
hoverProvider = createHoverHandler(projectFinder, sourceLinks, liveDataProvider);
|
||||
new SpringProcessLiveHoverUpdater(server, hoverProvider, projectFinder, liveDataProvider);
|
||||
|
||||
BootJavaConfig config = appContext.getBean(BootJavaConfig.class);
|
||||
|
||||
// deal with locally running processes and their connections
|
||||
SpringProcessConnectorLocal liveDataLocalProcessConnector = new SpringProcessConnectorLocal(liveDataService, projectObserver);
|
||||
SpringProcessConnectorLocal liveDataLocalProcessConnector = new SpringProcessConnectorLocal(liveDataService, projectObserver, config);
|
||||
|
||||
// create and handle commands
|
||||
new SpringProcessCommandHandler(server, liveDataService, liveDataLocalProcessConnector, appContext.getBeansOfType(SpringProcessConnectorRemote.class).values());
|
||||
|
||||
// track locally running processes and automatically connect to them if configured to do so
|
||||
BootJavaConfig config = appContext.getBean(BootJavaConfig.class);
|
||||
liveProcessTracker = new SpringProcessTracker(liveDataLocalProcessConnector, Duration.ofMillis(config.getLiveInformationAutomaticTrackingDelay()));
|
||||
|
||||
//
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019, 2022 Pivotal, Inc.
|
||||
* Copyright (c) 2019, 2024 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
|
||||
@@ -115,20 +115,19 @@ public class SpringProcessCommandHandler {
|
||||
});
|
||||
}
|
||||
|
||||
private CompletableFuture<Object> connect(ExecuteCommandParams params) {
|
||||
private CompletableFuture<?> connect(ExecuteCommandParams params) {
|
||||
String processKey = getProcessKey(params);
|
||||
if (processKey != null) {
|
||||
|
||||
// try local processes
|
||||
if (SpringProcessConnectorLocal.isAvailable()) {
|
||||
if (localProcessConnector.isAvailable()) {
|
||||
|
||||
// Try cached processes.
|
||||
SpringProcessDescriptor[] processes = localProcessConnector
|
||||
.getProcesses(false, SpringProcessStatus.REGULAR, SpringProcessStatus.AUTO_CONNECT);
|
||||
for (SpringProcessDescriptor process : processes) {
|
||||
if (process.getProcessKey().equals(processKey)) {
|
||||
localProcessConnector.connectProcess(process);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
return localProcessConnector.connectProcess(process);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +135,7 @@ public class SpringProcessCommandHandler {
|
||||
.getProcesses(true, SpringProcessStatus.REGULAR, SpringProcessStatus.AUTO_CONNECT);
|
||||
for (SpringProcessDescriptor process : processes) {
|
||||
if (process.getProcessKey().equals(processKey)) {
|
||||
localProcessConnector.connectProcess(process);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
return localProcessConnector.connectProcess(process);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,8 +147,7 @@ public class SpringProcessCommandHandler {
|
||||
for (RemoteBootAppData remoteProcess : remoteProcesses) {
|
||||
String key = SpringProcessConnectorRemote.getProcessKey(remoteProcess);
|
||||
if (processKey.equals(key)) {
|
||||
remoteProcessConnector.connectProcess(remoteProcess);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
return remoteProcessConnector.connectProcess(remoteProcess);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,26 +155,26 @@ public class SpringProcessCommandHandler {
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
private CompletableFuture<Object> refresh(ExecuteCommandParams params) {
|
||||
|
||||
private CompletableFuture<?> refresh(ExecuteCommandParams params) {
|
||||
SpringProcessParams springProcessParams = new SpringProcessParams();
|
||||
springProcessParams.setProcessKey(getProcessKey(params));
|
||||
springProcessParams.setEndpoint(getArgumentByKey(params, "endpoint"));
|
||||
if (springProcessParams.getProcessKey() != null) {
|
||||
connectorService.refreshProcess(springProcessParams);
|
||||
return connectorService.refreshProcess(springProcessParams);
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
private CompletableFuture<Object> refreshMetrics(ExecuteCommandParams params) {
|
||||
private CompletableFuture<?> refreshMetrics(ExecuteCommandParams params) {
|
||||
SpringProcessParams springProcessParams = new SpringProcessParams();
|
||||
springProcessParams.setProcessKey(getProcessKey(params));
|
||||
springProcessParams.setEndpoint(getArgumentByKey(params, "endpoint"));
|
||||
springProcessParams.setMetricName(getArgumentByKey(params, "metricName"));
|
||||
springProcessParams.setTags(getArgumentByKey(params, "tags")); // Convert tags to a map
|
||||
if (springProcessParams.getProcessKey() != null) {
|
||||
connectorService.refreshProcess(springProcessParams);
|
||||
return connectorService.refreshProcess(springProcessParams);
|
||||
}
|
||||
|
||||
return CompletableFuture.completedFuture(null);
|
||||
@@ -206,33 +203,54 @@ public class SpringProcessCommandHandler {
|
||||
result.add(new LiveProcessCommand(COMMAND_DISCONNECT, processKey, label, process.getProjectName(), process.getProcessId()));
|
||||
alreadyConnected.add(processKey);
|
||||
}
|
||||
|
||||
|
||||
// collect available remote process. Some of them might be local processes, make a note of these too
|
||||
List<LiveProcessCommand> remoteProcessCommands = new ArrayList<>();
|
||||
Map<String, LiveProcessCommand> localProcessCommands = new HashMap<>();
|
||||
for (SpringProcessConnectorRemote remoteProcessConnector : remoteProcessConnectors) {
|
||||
RemoteBootAppData[] remoteProcesses = remoteProcessConnector.getProcesses();
|
||||
for (RemoteBootAppData remoteProcess : remoteProcesses) {
|
||||
String processKey = SpringProcessConnectorRemote.getProcessKey(remoteProcess);
|
||||
boolean isLocal = remoteProcess.getProcessID() != null && ("localhost".equals(remoteProcess.getHost()) || "127.0.0.1".equals(remoteProcess.getHost()));
|
||||
if (alreadyConnected.contains(processKey)) {
|
||||
alreadyConnected.add(SpringProcessConnectorService.getProcessKey(remoteProcess.getProcessID(), remoteProcess.getProcessName()));
|
||||
} else {
|
||||
String label = createLabel(remoteProcess.getProcessID(), SpringProcessConnectorRemote.getProcessName(remoteProcess));
|
||||
LiveProcessCommand command = new LiveProcessCommand(COMMAND_CONNECT, processKey, label, remoteProcess.getProjectName(), remoteProcess.getProcessID());
|
||||
if (isLocal) {
|
||||
// Local add these later while checking for local boot processes
|
||||
localProcessCommands.put(remoteProcess.getProcessID(), command);
|
||||
} else {
|
||||
// Keep these to be added at the end
|
||||
remoteProcessCommands.add(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// other available local processes
|
||||
if (SpringProcessConnectorLocal.isAvailable()) {
|
||||
if (localProcessConnector.isAvailable()) {
|
||||
SpringProcessDescriptor[] localProcesses = localProcessConnector.getProcesses(true, SpringProcessStatus.REGULAR, SpringProcessStatus.AUTO_CONNECT);
|
||||
for (SpringProcessDescriptor localProcess : localProcesses) {
|
||||
String processKey = localProcess.getProcessKey();
|
||||
if (!alreadyConnected.contains(processKey)) {
|
||||
String label = createLabel(localProcess.getProcessID(), localProcess.getProcessName());
|
||||
|
||||
LiveProcessCommand command = new LiveProcessCommand(COMMAND_CONNECT, processKey, label, localProcess.getProjectName(), null);
|
||||
LiveProcessCommand command = localProcessCommands.remove(localProcess.getProcessID());
|
||||
if (command == null) {
|
||||
String label = createLabel(localProcess.getProcessID(), localProcess.getProcessName());
|
||||
command = new LiveProcessCommand(COMMAND_CONNECT, processKey, label, localProcess.getProjectName(), null);
|
||||
}
|
||||
result.add(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// other available remote processes
|
||||
for (SpringProcessConnectorRemote remoteProcessConnector : remoteProcessConnectors) {
|
||||
RemoteBootAppData[] remoteProcesses = remoteProcessConnector.getProcesses();
|
||||
for (RemoteBootAppData remoteProcess : remoteProcesses) {
|
||||
String processKey = SpringProcessConnectorRemote.getProcessKey(remoteProcess);
|
||||
if (!alreadyConnected.contains(processKey)) {
|
||||
String label = createLabel(remoteProcess.getProcessID(), SpringProcessConnectorRemote.getProcessName(remoteProcess));
|
||||
result.add(new LiveProcessCommand(COMMAND_CONNECT, processKey, label, null, remoteProcess.getProcessID()));
|
||||
}
|
||||
}
|
||||
for (LiveProcessCommand command : localProcessCommands.values()) {
|
||||
result.add(command);
|
||||
}
|
||||
|
||||
result.addAll(remoteProcessCommands);
|
||||
|
||||
log.debug("getProcessCommands => {}", result);
|
||||
return CompletableFuture.completedFuture((Object[]) result.toArray(new Object[result.size()]));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019, 2022 Pivotal, Inc.
|
||||
* Copyright (c) 2019, 2024 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
|
||||
@@ -26,6 +26,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.app.BootJavaConfig;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.SpringProjectUtil;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.ProjectObserver;
|
||||
@@ -36,7 +37,6 @@ import com.sun.tools.attach.VirtualMachineDescriptor;
|
||||
/**
|
||||
* @author Martin Lippert
|
||||
*/
|
||||
@SuppressWarnings("restriction")
|
||||
public class SpringProcessConnectorLocal {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringProcessConnectorLocal.class);
|
||||
@@ -52,8 +52,11 @@ public class SpringProcessConnectorLocal {
|
||||
|
||||
private boolean projectsChanged;
|
||||
|
||||
final private BootJavaConfig config;
|
||||
|
||||
|
||||
public SpringProcessConnectorLocal(SpringProcessConnectorService processConnector, ProjectObserver projectObserver) {
|
||||
public SpringProcessConnectorLocal(SpringProcessConnectorService processConnector, ProjectObserver projectObserver, BootJavaConfig config) {
|
||||
this.config = config;
|
||||
this.projects = new ConcurrentHashMap<>();
|
||||
this.processes = Collections.synchronizedSet(new HashSet<>());
|
||||
this.statusUpdateThreadPool = Executors.newFixedThreadPool(10);
|
||||
@@ -91,14 +94,17 @@ public class SpringProcessConnectorLocal {
|
||||
* checks whether this class can operate normally or not - it is recommended to check this before calling out to this class
|
||||
* (if the attach to VirtualMachine library is not around, this class cannot really do anything and will throw exceptions)
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
try {
|
||||
Class<?> vmClass = VirtualMachine.class;
|
||||
return vmClass != null;
|
||||
}
|
||||
catch (NoClassDefFoundError e) {
|
||||
return false;
|
||||
public boolean isAvailable() {
|
||||
if (config.isShowingAllJvmProcesses()) {
|
||||
try {
|
||||
Class<?> vmClass = VirtualMachine.class;
|
||||
return vmClass != null;
|
||||
}
|
||||
catch (NoClassDefFoundError e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isLocalProcess(String processKey) {
|
||||
@@ -186,7 +192,7 @@ public class SpringProcessConnectorLocal {
|
||||
}
|
||||
}
|
||||
|
||||
public void connectProcess(SpringProcessDescriptor descriptor) {
|
||||
public CompletableFuture<Void> connectProcess(SpringProcessDescriptor descriptor) {
|
||||
VirtualMachine vm = null;
|
||||
VirtualMachineDescriptor vmDescriptor = descriptor.getVm();
|
||||
|
||||
@@ -205,6 +211,7 @@ public class SpringProcessConnectorLocal {
|
||||
jmxAddress = vm.startLocalManagementAgent();
|
||||
} catch (Exception e) {
|
||||
log.error("Error starting local management agent", e);
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,11 +223,13 @@ public class SpringProcessConnectorLocal {
|
||||
SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX(ProcessType.LOCAL,
|
||||
descriptor.getProcessKey(), jmxAddress, urlScheme, processID, processName, descriptor.getProjectName(), null, null);
|
||||
|
||||
this.processConnectorService.connectProcess(descriptor.getProcessKey(), connector);
|
||||
return this.processConnectorService.connectProcess(descriptor.getProcessKey(), connector);
|
||||
}
|
||||
return CompletableFuture.failedFuture(new Exception("No JMX URL available!"));
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("exception while connecting to jvm process", e);
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
finally {
|
||||
if (vm != null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019, 2022 Pivotal, Inc.
|
||||
* Copyright (c) 2019, 2024 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
|
||||
@@ -18,6 +18,7 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -43,6 +44,7 @@ public class SpringProcessConnectorRemote {
|
||||
|
||||
private String processId;
|
||||
private String processName;
|
||||
private String projectName;
|
||||
|
||||
public String getJmxurl() {
|
||||
return jmxurl;
|
||||
@@ -100,11 +102,15 @@ public class SpringProcessConnectorRemote {
|
||||
this.processName = processName;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RemoteBootAppData [jmxurl=" + jmxurl + ", host=" + host + ", urlScheme=" + urlScheme + ", port="
|
||||
+ port + ", manualConnect=" + manualConnect + ", keepChecking=" + keepChecking + ", processId="
|
||||
+ processId + ", processName=" + processName + "]";
|
||||
+ processId + ", processName=" + processName + ". projectName=" + projectName + "]";
|
||||
}
|
||||
|
||||
public void setManualConnection(boolean manualConnect) {
|
||||
@@ -117,7 +123,7 @@ public class SpringProcessConnectorRemote {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(host, jmxurl, keepChecking, manualConnect, port, processId, processName, urlScheme);
|
||||
return Objects.hash(host, jmxurl, keepChecking, manualConnect, port, processId, processName, urlScheme, projectName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,7 +138,8 @@ public class SpringProcessConnectorRemote {
|
||||
return Objects.equals(host, other.host) && Objects.equals(jmxurl, other.jmxurl)
|
||||
&& keepChecking == other.keepChecking && manualConnect == other.manualConnect
|
||||
&& Objects.equals(port, other.port) && Objects.equals(processId, other.processId)
|
||||
&& Objects.equals(processName, other.processName) && Objects.equals(urlScheme, other.urlScheme);
|
||||
&& Objects.equals(processName, other.processName) && Objects.equals(urlScheme, other.urlScheme)
|
||||
&& Objects.equals(projectName, other.getProcessName());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -205,10 +212,10 @@ public class SpringProcessConnectorRemote {
|
||||
}
|
||||
|
||||
public static String getProcessKey(RemoteBootAppData appData) {
|
||||
return "remote process - " + appData.getJmxurl();
|
||||
return appData.getJmxurl();
|
||||
}
|
||||
|
||||
public void connectProcess(RemoteBootAppData remoteProcess) {
|
||||
public CompletableFuture<Void> connectProcess(RemoteBootAppData remoteProcess) {
|
||||
String processKey = getProcessKey(remoteProcess);
|
||||
String processID = remoteProcess.getProcessID();
|
||||
String processName = getProcessName(remoteProcess);
|
||||
@@ -216,14 +223,15 @@ public class SpringProcessConnectorRemote {
|
||||
String host = remoteProcess.getHost();
|
||||
String port = remoteProcess.getPort();
|
||||
String urlScheme = remoteProcess.getUrlScheme();
|
||||
String projectName = remoteProcess.getProjectName();
|
||||
// boolean keepChecking = _appData.isKeepChecking();
|
||||
|
||||
if (jmxURL.startsWith("http")) {
|
||||
SpringProcessConnectorOverHttp connector = new SpringProcessConnectorOverHttp(ProcessType.REMOTE, processKey, jmxURL, urlScheme, processID, processName, urlScheme, host, port);
|
||||
processConnectorService.connectProcess(processKey, connector);
|
||||
SpringProcessConnectorOverHttp connector = new SpringProcessConnectorOverHttp(ProcessType.REMOTE, processKey, jmxURL, urlScheme, processID, processName, projectName, host, port);
|
||||
return processConnectorService.connectProcess(processKey, connector);
|
||||
} else {
|
||||
SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX(ProcessType.REMOTE, processKey, jmxURL, urlScheme, processID, processName, null, host, port);
|
||||
processConnectorService.connectProcess(processKey, connector);
|
||||
SpringProcessConnectorOverJMX connector = new SpringProcessConnectorOverJMX(ProcessType.REMOTE, processKey, jmxURL, urlScheme, processID, processName, projectName, host, port);
|
||||
return processConnectorService.connectProcess(processKey, connector);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019, 2023 Pivotal, Inc.
|
||||
* Copyright (c) 2019, 2024 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
|
||||
@@ -11,6 +11,7 @@
|
||||
package org.springframework.ide.vscode.boot.java.livehover.v2;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
@@ -87,7 +88,7 @@ public class SpringProcessConnectorService {
|
||||
this.retryDelayInSeconds = retryDelayInSeconds;
|
||||
}
|
||||
|
||||
public void connectProcess(String processKey, SpringProcessConnector connector) {
|
||||
public CompletableFuture<Void> connectProcess(String processKey, SpringProcessConnector connector) {
|
||||
log.info("connect to process: " + processKey);
|
||||
|
||||
this.connectors.put(processKey, connector);
|
||||
@@ -95,17 +96,12 @@ public class SpringProcessConnectorService {
|
||||
|
||||
connector.addConnectorChangeListener(connectorListener);
|
||||
|
||||
try {
|
||||
final IndefiniteProgressTask progressTask = getProgressTask(
|
||||
"spring-process-connector-service-connect-" + processKey, "Connect", null);
|
||||
scheduleConnect(progressTask, processKey, connector, 0, TimeUnit.SECONDS, 0);
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("error connecting to " + processKey, e);
|
||||
}
|
||||
final IndefiniteProgressTask progressTask = getProgressTask(
|
||||
"spring-process-connector-service-connect-" + processKey, "Connect", null);
|
||||
return scheduleConnect(progressTask, processKey, connector, 0, TimeUnit.SECONDS, 0);
|
||||
}
|
||||
|
||||
public void refreshProcess(SpringProcessParams springProcessParams) {
|
||||
public CompletableFuture<Void> refreshProcess(SpringProcessParams springProcessParams) {
|
||||
log.info("refresh process: " + springProcessParams.getProcessKey());
|
||||
|
||||
SpringProcessConnector connector = this.connectors.get(springProcessParams.getProcessKey());
|
||||
@@ -113,8 +109,9 @@ public class SpringProcessConnectorService {
|
||||
final IndefiniteProgressTask progressTask = getProgressTask(
|
||||
"spring-process-connector-service-refresh-data-" + springProcessParams.getProcessKey(), "Refresh", null);
|
||||
|
||||
scheduleRefresh(progressTask, springProcessParams, connector, 0, TimeUnit.SECONDS, 0);
|
||||
return scheduleRefresh(progressTask, springProcessParams, connector, 0, TimeUnit.SECONDS, 0);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
public SpringProcessLiveData getLiveData(String processKey) {
|
||||
@@ -161,35 +158,35 @@ public class SpringProcessConnectorService {
|
||||
return processID;
|
||||
}
|
||||
|
||||
private void scheduleConnect(IndefiniteProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
|
||||
private CompletableFuture<Void> scheduleConnect(IndefiniteProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
|
||||
String progressMessage = "Connecting to process: " + processKey + " - retry no: " + retryNo;
|
||||
log.info(progressMessage);
|
||||
|
||||
|
||||
this.scheduler.schedule(() -> {
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
progressTask.progressEvent(progressMessage);
|
||||
connector.connect();
|
||||
progressTask.done();
|
||||
|
||||
refreshProcess(new SpringProcessParams(processKey, "", "", ""));
|
||||
progressTask.done();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.info("problem occured during process connect", e);
|
||||
|
||||
if (retryNo < maxRetryCount && isKnownProcessKey(processKey)) {
|
||||
scheduleConnect(progressTask, processKey, connector, retryDelayInSeconds, TimeUnit.SECONDS, retryNo + 1);
|
||||
} else {
|
||||
progressTask.done();
|
||||
|
||||
// Send message to client if maximum retries reached on error
|
||||
if (isKnownProcessKey(processKey)) {
|
||||
diagnosticService.diagnosticEvent(ShowMessageException
|
||||
.error("Failed to connect to process " + processKey + " after retries: " + retryNo, e));
|
||||
}
|
||||
}
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, delay, unit);
|
||||
}, CompletableFuture.delayedExecutor(delay, unit, scheduler)).thenCompose(v -> refreshProcess(new SpringProcessParams(processKey, "", "", ""))).exceptionallyCompose(e -> {
|
||||
log.info("problem occured during process connect", e);
|
||||
|
||||
if (retryNo < maxRetryCount && isKnownProcessKey(processKey)) {
|
||||
return scheduleConnect(progressTask, processKey, connector, retryDelayInSeconds, TimeUnit.SECONDS, retryNo + 1);
|
||||
} else {
|
||||
progressTask.done();
|
||||
|
||||
// Send message to client if maximum retries reached on error
|
||||
if (isKnownProcessKey(processKey)) {
|
||||
diagnosticService.diagnosticEvent(ShowMessageException
|
||||
.error("Failed to connect to process " + processKey + " after retries: " + retryNo, e));
|
||||
}
|
||||
return CompletableFuture.completedStage(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void scheduleDisconnect(IndefiniteProgressTask progressTask, String processKey, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
|
||||
@@ -220,16 +217,14 @@ public class SpringProcessConnectorService {
|
||||
}, delay, unit);
|
||||
}
|
||||
|
||||
private void scheduleRefresh(IndefiniteProgressTask progressTask, SpringProcessParams springProcessParams, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
|
||||
private CompletableFuture<Void> scheduleRefresh(IndefiniteProgressTask progressTask, SpringProcessParams springProcessParams, SpringProcessConnector connector, long delay, TimeUnit unit, int retryNo) {
|
||||
String processKey = springProcessParams.getProcessKey();
|
||||
String endpoint = springProcessParams.getEndpoint();
|
||||
String metricName = springProcessParams.getMetricName();
|
||||
String progressMessage = "Refreshing data from Spring process: " + processKey + " - retry no: " + retryNo;
|
||||
log.info(progressMessage);
|
||||
|
||||
|
||||
this.scheduler.schedule(() -> {
|
||||
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
progressTask.progressEvent(progressMessage);
|
||||
if(METRICS.equals(endpoint) && (MEMORY.equals(metricName))) {
|
||||
@@ -268,28 +263,32 @@ public class SpringProcessConnectorService {
|
||||
progressTask.done();
|
||||
}
|
||||
catch (Exception e) {
|
||||
|
||||
log.info("problem occured during process live data refresh", e);
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}, CompletableFuture.delayedExecutor(delay, unit, scheduler)).exceptionallyCompose(e -> {
|
||||
log.info("problem occured during process live data refresh", e);
|
||||
|
||||
if (retryNo < maxRetryCount && isKnownProcessKey(processKey)) {
|
||||
return scheduleRefresh(progressTask, springProcessParams, connector, retryDelayInSeconds, TimeUnit.SECONDS,
|
||||
retryNo + 1);
|
||||
}
|
||||
else {
|
||||
progressTask.done();
|
||||
|
||||
if (retryNo < maxRetryCount && isKnownProcessKey(processKey)) {
|
||||
scheduleRefresh(progressTask, springProcessParams, connector, retryDelayInSeconds, TimeUnit.SECONDS,
|
||||
retryNo + 1);
|
||||
}
|
||||
else {
|
||||
progressTask.done();
|
||||
|
||||
// Send message to client if maximum retries reached on error
|
||||
if (isKnownProcessKey(processKey)) {
|
||||
diagnosticService.diagnosticEvent(ShowMessageException
|
||||
.error("Failed to refresh live data from process " + processKey + " after retries: " + retryNo, e));
|
||||
|
||||
if (!connectedSuccess.containsKey(connector.getProcessKey())) {
|
||||
disconnectProcess(processKey);
|
||||
}
|
||||
// Send message to client if maximum retries reached on error
|
||||
if (isKnownProcessKey(processKey)) {
|
||||
diagnosticService.diagnosticEvent(ShowMessageException
|
||||
.error("Failed to refresh live data from process " + processKey + " after retries: " + retryNo, e));
|
||||
|
||||
if (!connectedSuccess.containsKey(connector.getProcessKey())) {
|
||||
disconnectProcess(processKey);
|
||||
}
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}, delay, unit);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private IndefiniteProgressTask getProgressTask(String prefixId, String title, String message) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2019 Pivotal, Inc.
|
||||
* Copyright (c) 2019, 2024 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
|
||||
@@ -31,7 +31,6 @@ public class SpringProcessTracker {
|
||||
private static final Logger log = LoggerFactory.getLogger(SpringProcessTracker.class);
|
||||
|
||||
private final SpringProcessConnectorLocal localProcessConnector;
|
||||
private final boolean isConnectorAvailable;
|
||||
|
||||
private boolean automaticTrackingEnabled;
|
||||
private Duration POLLING_INTERVAL;
|
||||
@@ -43,9 +42,7 @@ public class SpringProcessTracker {
|
||||
this.localProcessConnector = localProcessConnector;
|
||||
this.POLLING_INTERVAL = pollingInterval != null ? pollingInterval : Duration.ofMillis(BootJavaConfig.LIVE_INFORMATION_AUTOMATIC_TRACKING_DELAY_DEFAULT);
|
||||
this.automaticTrackingEnabled = false;
|
||||
this.processesAlreadySeen = new HashSet<>();
|
||||
|
||||
this.isConnectorAvailable = SpringProcessConnectorLocal.isAvailable();
|
||||
this.processesAlreadySeen = new HashSet<>();
|
||||
}
|
||||
|
||||
public synchronized void setTrackingEnabled(boolean trackingEnabled) {
|
||||
@@ -74,8 +71,8 @@ public class SpringProcessTracker {
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
if (!isConnectorAvailable) {
|
||||
log.error("virtual machine connector library not available, no automatic local process tracking possible");
|
||||
if (!localProcessConnector.isAvailable()) {
|
||||
log.error("No automatic local process tracking possible");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user