Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,4 @@
version="0.0.0"
unpack="false"/>

<plugin
id="org.springsource.ide.eclipse.commons.jdk_tools"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>

</feature>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
package org.springframework.ide.eclipse.boot.dash;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.net.proxy.IProxyService;
Expand Down Expand Up @@ -206,15 +207,21 @@ public void stateChanged(ILaunchConfiguration owner) {
for (IProcess p : l.getProcesses()) {
String pid = p.getAttribute(IProcess.ATTR_PROCESS_ID);
if (pid != null) {
CommandInfo cmd = new CommandInfo("sts/livedata/localAdd", Map.of(
"host", "127.0.0.1",
"urlScheme", "http",
"jmxurl", "service:jmx:rmi:///jndi/rmi://127.0.0.1:%s/jmxrmi".formatted(l.getAttribute(BootLaunchConfigurationDelegate.JMX_PORT)),
"manualConnect", !BootLaunchConfigurationDelegate.getAutoConnect(owner),
"processId", pid,
"processName", owner.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, p.getLabel()),
"projectName", project.getProject().getName()
));
Map<String, Object> liveDataArgs = new HashMap<>();
liveDataArgs.put("host", "127.0.0.1");
liveDataArgs.put("urlScheme", "http");
// Only a pinned, nonzero JMX port has a stable jmxurl. For the
// auto/dynamic port case no port is picked here; the language
// server attaches to 'processId' on-demand instead.
int jmxPort = BootLaunchConfigurationDelegate.getJMXPortAsInt(l);
if (jmxPort > 0) {
liveDataArgs.put("jmxurl", "service:jmx:rmi:///jndi/rmi://127.0.0.1:%s/jmxrmi".formatted(jmxPort));
}
liveDataArgs.put("manualConnect", !BootLaunchConfigurationDelegate.getAutoConnect(owner));
liveDataArgs.put("processId", pid);
liveDataArgs.put("processName", owner.getAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, p.getLabel()));
liveDataArgs.put("projectName", project.getProject().getName());
CommandInfo cmd = new CommandInfo("sts/livedata/localAdd", liveDataArgs);

LiveProcessCommandsExecutor.getDefault().executeCommand(cmd).subscribe();
}
Expand All @@ -224,9 +231,17 @@ public void stateChanged(ILaunchConfiguration owner) {
break;
case INACTIVE:
for (ILaunch l : DebugPlugin.getDefault().getLaunchManager().getLaunches()) {
if (l.getLaunchConfiguration() == owner && l.getAttribute(BootLaunchConfigurationDelegate.JMX_PORT) != null) {
String jmxUrl = "service:jmx:rmi:///jndi/rmi://127.0.0.1:%s/jmxrmi".formatted(l.getAttribute(BootLaunchConfigurationDelegate.JMX_PORT));
LiveProcessCommandsExecutor.getDefault().executeCommand("sts/livedata/localRemove", jmxUrl).subscribe();
if (l.getLaunchConfiguration() == owner) {
int jmxPort = BootLaunchConfigurationDelegate.getJMXPortAsInt(l);
// Must match the key used when the app was added above: jmxurl if a
// port was pinned, otherwise the process id (see PROCESS_ID, captured
// by BootProcessFactory for the auto/dynamic port case).
String processKey = jmxPort > 0
? "service:jmx:rmi:///jndi/rmi://127.0.0.1:%s/jmxrmi".formatted(jmxPort)
: l.getAttribute(BootLaunchConfigurationDelegate.PROCESS_ID);
if (processKey != null) {
LiveProcessCommandsExecutor.getDefault().executeCommand("sts/livedata/localRemove", processKey).subscribe();
}
}
}
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
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;
import org.springsource.ide.eclipse.commons.core.util.ProcessUtils;
import org.springsource.ide.eclipse.commons.frameworks.core.maintype.MainTypeFinder;
import org.springsource.ide.eclipse.commons.livexp.core.AsyncLiveExpression;
import org.springsource.ide.eclipse.commons.livexp.core.DisposeListener;
Expand Down Expand Up @@ -518,7 +519,30 @@ public String toString() {
}

protected ActuatorClient getActuatorClient() {
return JMXActuatorClient.forPort(getTypeLookup(), this::getJmxPort);
return JMXActuatorClient.forUrl(getTypeLookup(), this::getJmxUrl);
}

/**
* JMX URL for this element's running process: a fixed jmxurl if a JMX port was pinned,
* otherwise attaches to the process id on-demand to resolve its local management agent's
* URL (see ProcessUtils.getLocalManagementAgentUrl).
*/
private String getJmxUrl() {
for (ILaunchConfiguration c : getLaunchConfigs()) {
for (ILaunch l : LaunchUtils.getLaunches(c)) {
if (!l.isTerminated()) {
int port = BootLaunchConfigurationDelegate.getJMXPortAsInt(l);
if (port>0) {
return JMXClient.createLocalJmxUrl(port);
}
String pid = l.getAttribute(BootLaunchConfigurationDelegate.PROCESS_ID);
if (pid!=null) {
return ProcessUtils.getLocalManagementAgentUrl(pid);
}
}
}
}
return null;
}

@Override
Expand Down Expand Up @@ -602,13 +626,13 @@ public Failable<ImmutableSet<String>> getLiveProfiles() {
if (liveProfiles == null) {
liveProfiles = PollingLiveExp.create(Failable.<ImmutableSet<String>>error(MissingLiveInfoMessages.NOT_YET_COMPUTED), () -> {
try {
String localJmxUrl = JMXClient.createLocalJmxUrl(getJmxPort());
if (localJmxUrl == null) {
String processKey = getProcessKey();
if (processKey == 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
"processKey", processKey
)).get().orElse(new String[0]);
liveProfiles.refreshOnce();
return Failable.of(ImmutableSet.copyOf(o));
Expand All @@ -632,18 +656,27 @@ public Failable<ImmutableSet<String>> getLiveProfiles() {
}
}

private int getJmxPort() {
/**
* Key identifying this element's running process to the language server's live data
* connections: a jmxurl if a fixed JMX port was pinned, otherwise the process id (the
* language server attaches to it on-demand; see PROCESS_ID / SpringApplicationLifeCycleClientManager).
*/
private String getProcessKey() {
for (ILaunchConfiguration c : getLaunchConfigs()) {
for (ILaunch l : LaunchUtils.getLaunches(c)) {
if (!l.isTerminated()) {
int port = BootLaunchConfigurationDelegate.getJMXPortAsInt(l);
if (port>0) {
return port;
return JMXClient.createLocalJmxUrl(port);
}
String pid = l.getAttribute(BootLaunchConfigurationDelegate.PROCESS_ID);
if (pid!=null) {
return pid;
}
}
}
}
return -1;
return null;
}

private int getLivePort(String propName) {
Expand All @@ -659,35 +692,33 @@ private int getLivePort(String propName) {
for (ILaunch l : BootLaunchUtils.getLaunches(conf)) {
if (!l.isTerminated()) {
debug("["+this.getName()+"] getLivePort("+propName+") found a launch");
int jmxPort = BootLaunchConfigurationDelegate.getJMXPortAsInt(l);
debug("["+this.getName()+"] getLivePort("+propName+") jmxPort = "+jmxPort);
if (jmxPort>0) {
SpringApplicationLifeCycleClientManager cm = null;
try {
cm = new SpringApplicationLifeCycleClientManager(jmxPort);
SpringApplicationLifecycleClient c = cm.getLifeCycleClient();
debug("["+this.getName()+"] getLivePort("+propName+") lifeCycleClient = "+c);
if (c!=null) {
//Just because lifecycle bean is ready does not mean that the port property has already been set.
//To avoid race condition we should wait here until the port is set (some apps aren't web apps and
//may never get a port set, so we shouldn't wait indefinitely!)
return RetryUtil.retry(100, 1000, () -> {
debug("["+this.getName()+"] getLivePort("+propName+") trying to get...");
int port = c.getProperty(propName, -1);
debug("["+this.getName()+"] getLivePort("+propName+") port = "+ port);
if (port<=0) {
throw new IllegalStateException("port not (yet) set");
}
return port;
});
}
} catch (Exception e) {
debug(ExceptionUtil.getMessage(e));
//most likely this just means the app isn't running so ignore
} finally {
if (cm!=null) {
cm.disposeClient();
}
// Uses a pinned JMX port directly if one was set, otherwise attaches to
// the launch's process id on-demand (see SpringApplicationLifeCycleClientManager).
SpringApplicationLifeCycleClientManager cm = null;
try {
cm = new SpringApplicationLifeCycleClientManager(l);
SpringApplicationLifecycleClient c = cm.getLifeCycleClient();
debug("["+this.getName()+"] getLivePort("+propName+") lifeCycleClient = "+c);
if (c!=null) {
//Just because lifecycle bean is ready does not mean that the port property has already been set.
//To avoid race condition we should wait here until the port is set (some apps aren't web apps and
//may never get a port set, so we shouldn't wait indefinitely!)
return RetryUtil.retry(100, 1000, () -> {
debug("["+this.getName()+"] getLivePort("+propName+") trying to get...");
int port = c.getProperty(propName, -1);
debug("["+this.getName()+"] getLivePort("+propName+") port = "+ port);
if (port<=0) {
throw new IllegalStateException("port not (yet) set");
}
return port;
});
}
} catch (Exception e) {
debug(ExceptionUtil.getMessage(e));
//most likely this just means the app isn't running so ignore
} finally {
if (cm!=null) {
cm.disposeClient();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2019 Pivotal, Inc.
* Copyright (c) 2017, 2026 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
Expand Down Expand Up @@ -63,10 +63,6 @@ public OperationInfo(String objectName, String operationName, String version) {

private JMXClient client = null;

public static JMXActuatorClient forPort(TypeLookup typeLookup, Supplier<Integer> jmxPort) {
return new JMXActuatorClient(typeLookup, () -> JMXClient.createLocalJmxUrl(jmxPort.get()));
}

public static JMXActuatorClient forUrl(TypeLookup typeLookup, Supplier<String> jmxUrl) {
return new JMXActuatorClient(typeLookup, jmxUrl);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@
import org.springframework.ide.eclipse.boot.launch.livebean.JmxBeanSupport.Feature;
import org.springframework.ide.eclipse.boot.launch.process.BootProcessFactory;
import org.springframework.ide.eclipse.boot.launch.profiles.ProfileHistory;
import org.springframework.ide.eclipse.boot.launch.util.PortFinder;
import org.springsource.ide.eclipse.commons.core.util.OsUtils;
import org.springsource.ide.eclipse.commons.core.util.StringUtil;
import org.springsource.ide.eclipse.commons.livexp.util.ExceptionUtil;
Expand Down Expand Up @@ -236,16 +235,20 @@ public String getVMArguments(ILaunchConfiguration conf)
} catch (Exception e) {
//ignore: bad data in launch config.
}
if (port==0) {
port = PortFinder.findFreePort(); //slightly better than calling JmxBeanSupport.randomPort()
}
// port==0 means 'auto': no port is picked here. The child JVM starts without any
// com.sun.management.jmxremote* args, and the JMX agent is started later
// on-demand via Attach API against the child's real PID (see BootProcessFactory /
// SpringApplicationLifeCycleClientManager). An explicitly pinned nonzero port
// keeps using the old fixed-port args.
String[] enableLiveBeanArgs = DebugPlugin.parseArguments(JmxBeanSupport.jmxBeanVmArgs(port, enabled));
for (int i = 0; i < enableLiveBeanArgs.length; i++) {
vmArgs.add(i, enableLiveBeanArgs[i]);
}
ILaunch currentLaunch = CURRENT_LAUNCH.get();
if (currentLaunch!=null) {
currentLaunch.setAttribute(JMX_PORT, ""+port);
if (port!=0) {
ILaunch currentLaunch = CURRENT_LAUNCH.get();
if (currentLaunch!=null) {
currentLaunch.setAttribute(JMX_PORT, ""+port);
}
}
}
// Fast startup VM args
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2017, 2020 Pivotal Software, Inc.
* Copyright (c) 2017, 2026 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
Expand Down Expand Up @@ -41,7 +41,6 @@
import org.springframework.ide.eclipse.boot.util.version.Version;
import org.springframework.ide.eclipse.boot.util.version.VersionParser;
import org.springframework.ide.eclipse.boot.util.version.VersionRange;
import org.springsource.ide.eclipse.commons.core.util.ProcessUtils;
import org.springsource.ide.eclipse.commons.livexp.util.Log;

/**
Expand All @@ -60,8 +59,6 @@ public class CloudCliServiceLaunchConfigurationDelegate extends BootCliLaunchCon
public final static String ATTR_CLOUD_SERVICE_ID = "local-cloud-service-id";

private final static String PREF_DONT_SHOW_PLATFORM_WARNING = "org.springframework.ide.eclipse.boot.launch.cloud.cli.NotSupportedPlatform";
private final static String PREF_DONT_SHOW_JRE_WARNING = "org.springframework.ide.eclipse.boot.launch.cloud.cli.JRE";
private final static String PREF_DONT_SHOW_JDK_WARNING = "org.springframework.ide.eclipse.boot.launch.cloud.cli.JDK";

private List<String> getCloudCliServiceLifeCycleVmArguments(ILaunchConfiguration configuration, int jmxPort) {
List<String> vmArgs = new ArrayList<>();
Expand Down Expand Up @@ -240,33 +237,7 @@ public IProcess newProcess(ILaunch launch, Process process, String label, Map<St
// Set invalid PID initially thus if PID is failed to be calculated then set PID launch attribute to invalid PID to fallback to default non-JMX process tracking
long pid = -1;
try {
if (ProcessUtils.isLatestJdkForTools()) {
pid = ProcessUtils.getProcessID(process);
} else {
Log.warn("Old JDK version. Need latest JDK to make JMX connection to process using its PID");
if (!store.getBoolean(PREF_DONT_SHOW_JDK_WARNING)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openWarning(
Display.getCurrent().getActiveShell(), "Cloud CLI Service Info Limitation",
"Cloud service process life-cycle data is limited and port data is unavailable because STS runnning on an old JDK version. Point STS to the latest JDK and restart it to have complete service process life-cycle and port data",
"Don't show this message again",
false, null, null);
store.setValue(PREF_DONT_SHOW_JDK_WARNING, dialog.getToggleState());
});
}
}
} catch (NoClassDefFoundError e) {
Log.warn(e);
if (!store.getBoolean(PREF_DONT_SHOW_JRE_WARNING)) {
PlatformUI.getWorkbench().getDisplay().asyncExec(() -> {
MessageDialogWithToggle dialog = MessageDialogWithToggle.openWarning(
Display.getCurrent().getActiveShell(), "Cloud CLI Service Info Limitation",
"Cloud service process life-cycle data is limited and port data is unavailable because STS is running on a JRE. Point it to a JDK and restart STS for complete service process life-cycle and port data",
"Don't show this message again",
false, null, null);
store.setValue(PREF_DONT_SHOW_JRE_WARNING, dialog.getToggleState());
});
}
pid = process.pid();
} catch (UnsupportedOperationException e) {
Log.warn(e);
if (!store.getBoolean(PREF_DONT_SHOW_PLATFORM_WARNING)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public static String jmxBeanVmArgs(String jmxPort, EnumSet<Feature> enabled) {
if (!enabled.isEmpty()) {
//At least one feature enabled
StringBuilder str = new StringBuilder();
for (String a : enableJmxArgs(jmxPort)) {
String[] args = "0".equals(jmxPort) ? autoJmxArgs() : enableJmxArgs(jmxPort);
for (String a : args) {
str.append(a+"\n");
}
for (Feature feature : enabled) {
Expand Down Expand Up @@ -80,6 +81,19 @@ public static String[] enableJmxArgs(String jmxPort) {
};
}

/**
* VM args for the auto/dynamic port case (jmxPort == 0). No
* {@code com.sun.management.jmxremote*} args are emitted here: rather than picking a port
* up front, the JMX agent is started on-demand later via the Attach API against the
* child's real OS PID, once the child actually exists and can bind its own port.
*/
public static String[] autoJmxArgs() {
return new String[] {
"-Dspring.jmx.enabled=true",
"-Dmanagement.endpoints.jmx.exposure.include=*"
};
}

public static final String JMX_PORT_PROP = "com.sun.management.jmxremote.port";
public static final String LAUNCH_CONFIG_TYPE_ID = BootLaunchConfigurationDelegate.TYPE_ID;
public static int randomPort() {
Expand Down
Loading
Loading