Show active profile for app in BootDash

This commit is contained in:
aboyko
2024-04-06 01:48:37 -04:00
parent f52967d9f8
commit 9cd237b97a
11 changed files with 158 additions and 14 deletions

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2023 Pivotal, Inc.
* Copyright (c) 2015, 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
@@ -15,6 +15,7 @@ import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashC
import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.HOST;
import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.INSTANCES;
import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.LIVE_PORT;
import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.ACTIVE_PROFILES;
import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.NAME;
import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.PROGRESS;
import static org.springframework.ide.eclipse.boot.dash.views.sections.BootDashColumn.PROJECT;
@@ -40,6 +41,7 @@ import org.springframework.ide.eclipse.boot.dash.model.BootDashElement;
import org.springframework.ide.eclipse.boot.dash.model.BootDashModel;
import org.springframework.ide.eclipse.boot.dash.model.ButtonModel;
import org.springframework.ide.eclipse.boot.dash.model.ClasspathPropertyTester;
import org.springframework.ide.eclipse.boot.dash.model.Failable;
import org.springframework.ide.eclipse.boot.dash.model.RefreshState;
import org.springframework.ide.eclipse.boot.dash.model.RunState;
import org.springframework.ide.eclipse.boot.dash.model.TagUtils;
@@ -444,6 +446,36 @@ public class BootDashLabels implements Disposable {
styledLabel = new StyledString(textLabel, stylers.color(color));
}
}
} else if (column == ACTIVE_PROFILES) {
RunState runState = element.getRunState();
ImmutableSet<String> activeProfiles = ImmutableSet.of();
boolean liveProfilesAvailable = false;
if (runState == RunState.RUNNING || runState == RunState.DEBUGGING) {
Failable<ImmutableSet<String>> liveProfiles = element.getLiveProfiles();
if (!liveProfiles.hasFailed()) {
activeProfiles = liveProfiles.getValue();
liveProfilesAvailable = true;
} else {
activeProfiles = element.getActiveProfiles();
}
} else {
activeProfiles = element.getActiveProfiles();
}
String textLabel;
if (activeProfiles.isEmpty() || (activeProfiles.size() == 1 && activeProfiles.iterator().next().isBlank())) {
textLabel = "";
} else {
StringBuilder str = new StringBuilder();
str.append(activeProfiles.size() == 1 ? "profile: " : "profiles: ");
str.append(String.join(",", activeProfiles));
textLabel = str.toString();
}
if (stylers == null) {
label = textLabel;
} else {
Color color = liveProfilesAvailable ? colorGreen() : colorGrey();
styledLabel = new StyledString(textLabel, stylers.color(color));
}
} else if (column==DEFAULT_PATH) {
String path = element.getDefaultRequestMappingPath();
if (stylers == null) {

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2023 Pivotal, Inc.
* Copyright (c) 2015, 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
@@ -14,6 +14,7 @@ import java.time.Duration;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -38,9 +39,11 @@ import org.springframework.ide.eclipse.boot.dash.util.CollectionUtils;
import org.springframework.ide.eclipse.boot.dash.util.LaunchConfRunStateTracker;
import org.springframework.ide.eclipse.boot.dash.util.RunStateTracker.RunStateListener;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.BootLsCommandUtils;
import org.springframework.ide.eclipse.boot.launch.cli.CloudCliServiceLaunchConfigurationDelegate;
import org.springframework.ide.eclipse.boot.launch.util.BootDebugUITools;
import org.springframework.ide.eclipse.boot.launch.util.BootLaunchUtils;
import org.springframework.ide.eclipse.boot.launch.util.JMXClient;
import org.springframework.ide.eclipse.boot.launch.util.SpringApplicationLifeCycleClientManager;
import org.springframework.ide.eclipse.boot.launch.util.SpringApplicationLifecycleClient;
import org.springframework.ide.eclipse.boot.util.RetryUtil;
@@ -58,6 +61,7 @@ import org.springsource.ide.eclipse.commons.ui.launch.LaunchUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.gson.reflect.TypeToken;
/**
* Abstracts out the commonalities between {@link BootProjectDashElement} and {@link LaunchConfDashElement}. Each can
@@ -91,6 +95,7 @@ public abstract class AbstractLaunchConfigurationsDashElement<T> extends Wrappin
private PollingLiveExp<Failable<ImmutableList<RequestMapping>>> liveRequestMappings;
private PollingLiveExp<Failable<LiveBeansModel>> liveBeans;
private PollingLiveExp<Failable<LiveEnvModel>> liveEnv;
private PollingLiveExp<Failable<ImmutableSet<String>>> liveProfiles;
public AbstractLaunchConfigurationsDashElement(LocalBootDashModel bootDashModel, T delegate) {
super(bootDashModel, delegate);
@@ -591,6 +596,42 @@ public abstract class AbstractLaunchConfigurationsDashElement<T> extends Wrappin
}
}
@Override
public Failable<ImmutableSet<String>> getLiveProfiles() {
synchronized (this) {
if (liveProfiles == null) {
liveProfiles = PollingLiveExp.create(Failable.<ImmutableSet<String>>error(MissingLiveInfoMessages.NOT_YET_COMPUTED), () -> {
try {
String localJmxUrl = JMXClient.createLocalJmxUrl(getJmxPort());
if (localJmxUrl == null) {
return Failable.error(getBootDashModel().getRunTarget().getType().getMissingLiveInfoMessages().getMissingInfoMessage(getName(), "profiles"));
}
String[] o = BootLsCommandUtils.executeCommand(TypeToken.get(String[].class), "sts/livedata/get", Map.of(
"endpoint", "profiles",
"processKey", localJmxUrl
)).get().orElse(new String[0]);
liveProfiles.refreshOnce();
return Failable.of(ImmutableSet.copyOf(o));
} catch (Exception e) {
return Failable.error(getBootDashModel().getRunTarget().getType().getMissingLiveInfoMessages().getMissingInfoMessage(getName(), "profiles"));
}
});
addElementState(liveProfiles);
addDisposableChild(liveProfiles);
runState.addListener((e, runstate) -> {
if (READY_STATES.contains(runstate)) {
// After the app is running refresh for 5 sec every 1 sec until active profiles are fetched
liveProfiles.sleepBetweenRefreshes(Duration.ofSeconds(1));
liveProfiles.refreshFor(Duration.ofSeconds(5));
} else {
liveProfiles.refreshOnce();
}
});
}
return liveProfiles.getValue();
}
}
private int getJmxPort() {
for (ILaunchConfiguration c : getLaunchConfigs()) {
for (ILaunch l : LaunchUtils.getLaunches(c)) {
@@ -684,4 +725,16 @@ public abstract class AbstractLaunchConfigurationsDashElement<T> extends Wrappin
return (LocalRunTarget) super.getTarget();
}
@Override
public ImmutableSet<String> getActiveProfiles() {
ILaunchConfiguration launchConfig = getActiveConfig();
if (launchConfig != null) {
String profile = BootLaunchConfigurationDelegate.getProfile(launchConfig);
if (profile != null) {
return ImmutableSet.of(profile);
}
}
return ImmutableSet.of();
}
}

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2020 Pivotal, Inc.
* Copyright (c) 2015, 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
@@ -70,6 +70,11 @@ public interface BootDashElement extends App, Taggable {
*/
Failable<LiveEnvModel> getLiveEnv();
/**
* Get the live profiles from a running process
*/
Failable<ImmutableSet<String>> getLiveProfiles();
/**
* Get the 'active' launch configuration. This may be null.
* <p>
@@ -109,6 +114,7 @@ public interface BootDashElement extends App, Taggable {
ObservableSet<BootDashElement> getChildren();
ImmutableSet<ILaunchConfiguration> getLaunchConfigs();
ImmutableSet<Integer> getLivePorts();
ImmutableSet<String> getActiveProfiles();
/**
* Fetch the parent of a BDE. If this is a nested BDE then the parent will be

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2021 Pivotal, Inc.
* Copyright (c) 2015, 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
@@ -71,7 +71,7 @@ public class BootProjectDashElement extends AbstractLaunchConfigurationsDashElem
private ObservableSet<BootDashElement> children;
private ObservableSet<Integer> ports;
private AsyncLiveExpression<Boolean> hasMainMethod = new AsyncLiveExpression<Boolean>(
private AsyncLiveExpression<Boolean> hasMainMethod = new AsyncLiveExpression<>(
false,
"Search main' method in project " + (getProject() != null && getProject().getName() != null ? "'" + getProject().getName() + "'": "UNKNOWQN"),
null,

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2017 Pivotal, Inc.
* Copyright (c) 2015, 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
@@ -162,7 +162,7 @@ public class LocalCloudServiceDashElement extends AbstractLaunchConfigurationsDa
protected LiveExpression<Integer> createPortExpression(final LiveExpression<RunState> runState) {
if (CloudCliServiceLaunchConfigurationDelegate.isSingleProcessServiceConfig(getActiveConfig())) {
AsyncLiveExpression<Integer> exp = new AsyncLiveExpression<Integer>(-1, "Refreshing port info for "+getName()) {
AsyncLiveExpression<Integer> exp = new AsyncLiveExpression<>(-1, "Refreshing port info for "+getName()) {
{
//Doesn't really depend on runState, but should be recomputed when runState changes.
dependsOn(runState);

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2023 Pivotal, Inc.
* Copyright (c) 2015, 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
@@ -27,6 +27,7 @@ public class LocalRunTarget extends AbstractRunTarget<Void> {
NAME,
DEVTOOLS,
LIVE_PORT,
ACTIVE_PROFILES,
INSTANCES,
DEFAULT_PATH,
TAGS

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Pivotal Software, Inc.
* Copyright (c) 2020, 2024 Pivotal Software, 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
@@ -13,6 +13,7 @@ package org.springframework.ide.eclipse.boot.dash.model.remote;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
@@ -66,6 +67,7 @@ import org.springframework.ide.eclipse.boot.dash.model.actuator.ActuatorClient;
import org.springframework.ide.eclipse.boot.dash.model.actuator.JMXActuatorClient;
import org.springframework.ide.eclipse.boot.dash.model.actuator.RequestMapping;
import org.springframework.ide.eclipse.boot.dash.model.actuator.env.LiveEnvModel;
import org.springframework.ide.eclipse.boot.launch.BootLsCommandUtils;
import org.springsource.ide.eclipse.commons.core.pstore.IPropertyStore;
import org.springsource.ide.eclipse.commons.core.pstore.PropertyStoreApi;
import org.springsource.ide.eclipse.commons.core.pstore.PropertyStores;
@@ -85,6 +87,7 @@ import org.springsource.ide.eclipse.commons.livexp.util.OldValueDisposer;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.gson.reflect.TypeToken;
public class GenericRemoteAppElement extends WrappingBootDashElement<String> implements Deletable, AppContext, Styleable, ElementStateListener, JmxConnectable, LiveDataCapableElement {
@@ -457,6 +460,10 @@ public class GenericRemoteAppElement extends WrappingBootDashElement<String> imp
return getRunStateExp().getValue();
}
public ImmutableSet<String> getActiveProfiles() {
return ImmutableSet.of();
}
private LiveExpression<RunState> getRunStateExp() {
return jmxRunStateTracker.augmentedRunState;
}
@@ -681,6 +688,7 @@ public class GenericRemoteAppElement extends WrappingBootDashElement<String> imp
private LiveExpression<Failable<ImmutableList<RequestMapping>>> liveRequestMappings;
private LiveExpression<Failable<LiveEnvModel>> liveEnv;
private LiveExpression<Failable<LiveBeansModel>> liveBeans;
private LiveExpression<Failable<ImmutableSet<String>>> liveProfiles;
@Override
public Failable<ImmutableList<RequestMapping>> getLiveRequestMappings() {
@@ -715,6 +723,43 @@ public class GenericRemoteAppElement extends WrappingBootDashElement<String> imp
return liveRequestMappings.getValue();
}
@Override
public Failable<ImmutableSet<String>> getLiveProfiles() {
synchronized (this) {
if (liveProfiles == null) {
final LiveExpression<Set<String>> actuatorUrls = getActuatorUrls();
liveProfiles = new AsyncLiveExpression<>(Failable.error(MissingLiveInfoMessages.NOT_YET_COMPUTED), "Fetch active profiles for '"+getStyledName(null).getString()+"'") {
@Override
protected Failable<ImmutableSet<String>> compute() {
Set<String> targets = actuatorUrls.getValue();
if (targets!=null && !targets.isEmpty()) {
if (targets.size() == 1) {
String target = targets.iterator().next();
try {
String[] o = BootLsCommandUtils.executeCommand(TypeToken.get(String[].class), "sts/livedata/get", Map.of(
"endpoint", "profiles",
"processKey", target
)).get().orElse(new String[0]);
return Failable.of(ImmutableSet.copyOf(o));
} catch (Exception e) {
return Failable.error(getBootDashModel().getRunTarget().getType().getMissingLiveInfoMessages().getMissingInfoMessage(getStyledName(null).getString(), "profiles"));
}
} else {
return Failable.error(buffer -> buffer.p("More than one child can provide live data. Please select one."));
}
}
return Failable.error(getBootDashModel().getRunTarget().getType().getMissingLiveInfoMessages().getMissingInfoMessage(getStyledName(null).getString(), "profiles"));
}
};
liveProfiles.dependsOn(actuatorUrls);
addElementState(liveProfiles);
addDisposableChild(liveProfiles);
}
}
return liveProfiles.getValue();
}
@Override
public Failable<LiveEnvModel> getLiveEnv() {
synchronized (this) {

View File

@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2015, 2023 Pivotal, Inc.
* Copyright (c) 2015, 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
@@ -29,6 +29,7 @@ public class BootDashColumn {
public static final BootDashColumn NAME = new BootDashColumn("NAME");
public static final BootDashColumn HOST = new BootDashColumn("HOST");
public static final BootDashColumn LIVE_PORT = new BootDashColumn("LIVE_PORT");
public static final BootDashColumn ACTIVE_PROFILES = new BootDashColumn("ACTIVE_PROFILES");
public static final BootDashColumn DEFAULT_PATH = new BootDashColumn("DEFAULT_PATH");
public static final BootDashColumn TAGS = new BootDashColumn("TAGS");
public static final BootDashColumn DEVTOOLS = new BootDashColumn("DEVTOOLS");