Prefer connecting via JMX to process. New UI for show/refresh/hide

This commit is contained in:
aboyko
2024-04-02 10:31:23 -04:00
parent 88ae9318ed
commit eb3d46d400
28 changed files with 622 additions and 189 deletions

View File

@@ -0,0 +1 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512.008 512.008" xml:space="preserve" fill="#000000"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <g> <g> <path style="fill:#868491;" d="M280.01,464.008h-48c-4.422,0-8,3.578-8,8v8c0,17.648,14.352,32,32,32s32-14.352,32-32v-8 C288.01,467.586,284.432,464.008,280.01,464.008z"></path> </g> <g> <path style="fill:#5C546A;" d="M256.002,464.008H232.01c-4.422,0-8,3.578-8,8v8c0,17.646,14.347,31.995,31.992,31.999 L256.002,464.008L256.002,464.008z"></path> </g> <g> <path style="fill:#B4B6BC;" d="M381.983,49.895c-37.391-35.141-86.367-52.742-137.734-49.52 C150.639,6.2,75.014,84.286,72.092,178.149c-2.156,69.547,34.375,133.578,95.344,167.117c15.156,8.344,24.57,24.367,24.57,41.828 v60.914c0,17.648,14.352,32,32,32h64c17.648,0,32-14.352,32-32v-60.914c0-17.445,9.484-33.508,24.766-41.93 c58.742-32.406,95.234-94.156,95.234-161.156C440.006,133.43,418.858,84.547,381.983,49.895z"></path> </g> <g> <circle style="fill:#FFFFFF;" cx="256.002" cy="184.008" r="128"></circle> </g> <g> <g> <path style="fill:#868491;" d="M283.986,232.008c-10.633,0-16.07-6.203-20.031-10.727c-3.492-3.984-4.883-5.273-8-5.273 c-3.102,0-4.484,1.289-7.961,5.266c-3.734,4.273-9.383,10.734-20.016,10.734c-10.625,0-16.273-6.461-20.008-10.734 c-3.477-3.977-4.859-5.266-7.961-5.266c-4.422,0-8-3.578-8-8s3.578-8,8-8c10.625,0,16.273,6.461,20.008,10.734 c3.477,3.977,4.859,5.266,7.961,5.266s4.492-1.289,7.969-5.266c3.734-4.273,9.383-10.734,20.008-10.734 c10.633,0,16.07,6.203,20.031,10.727c3.492,3.984,4.883,5.273,8,5.273c3.109,0,4.5-1.289,7.992-5.266 c3.742-4.273,9.391-10.734,20.031-10.734c4.422,0,8,3.578,8,8s-3.578,8-8,8c-3.117,0-4.508,1.289-8,5.273 C300.267,225.555,294.619,232.008,283.986,232.008z"></path> </g> </g> <g> <g> <path style="fill:#5C546A;" d="M232.002,384.008c-3.68,0-6.992-2.555-7.813-6.297l-40-184c-0.938-4.32,1.805-8.578,6.117-9.523 c4.344-0.953,8.578,1.805,9.523,6.117l40,184c0.938,4.32-1.805,8.578-6.117,9.523 C233.135,383.954,232.564,384.008,232.002,384.008z"></path> </g> </g> <g> <g> <path style="fill:#5C546A;" d="M280.017,384.008c-0.563,0-1.133-0.055-1.711-0.18c-4.313-0.945-7.055-5.203-6.117-9.523l40-184 c0.945-4.313,5.164-7.063,9.523-6.117c4.313,0.945,7.055,5.203,6.117,9.523l-40,184 C287.01,381.454,283.697,384.008,280.017,384.008z"></path> </g> </g> <g> <path style="fill:#B4B6BC;" d="M190.633,376.008c0.851,3.593,1.374,7.293,1.374,11.086v60.914c0,17.648,14.352,32,32,32h64 c17.648,0,32-14.352,32-32v-60.914c0-3.79,0.527-7.491,1.384-11.086H190.633z"></path> </g> <g> <path style="fill:#868491;" d="M256.002,376.008h-65.369c0.851,3.593,1.374,7.293,1.374,11.086v60.914c0,17.648,14.352,32,32,32 h31.995V376.008z"></path> </g> <g> <g> <rect x="256.002" y="400.008" style="fill:#868491;" width="64" height="16"></rect> </g> </g> <g> <g> <rect x="256.006" y="432.008" style="fill:#868491;" width="64" height="16"></rect> </g> </g> <g> <g> <rect x="192.002" y="400.008" style="fill:#5C546A;" width="64" height="16"></rect> </g> </g> <g> <g> <rect x="192.002" y="432.008" style="fill:#5C546A;" width="64" height="16"></rect> </g> </g> </g> </g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512.008 512.008" xml:space="preserve">
<g>
<g>
<path style="fill:#868491;" d="M280.01,464.008h-48c-4.422,0-8,3.578-8,8v8c0,17.648,14.352,32,32,32s32-14.352,32-32v-8
C288.01,467.586,284.432,464.008,280.01,464.008z"/>
</g>
<g>
<path style="fill:#5C546A;" d="M256.002,464.008H232.01c-4.422,0-8,3.578-8,8v8c0,17.646,14.347,31.995,31.992,31.999
L256.002,464.008L256.002,464.008z"/>
</g>
<g>
<path style="fill:#FFCD00;" d="M381.983,49.895c-37.391-35.141-86.367-52.742-137.734-49.52
C150.639,6.2,75.014,84.286,72.092,178.149c-2.156,69.547,34.375,133.578,95.344,167.117c15.156,8.344,24.57,24.367,24.57,41.828
v60.914c0,17.648,14.352,32,32,32h64c17.648,0,32-14.352,32-32v-60.914c0-17.445,9.484-33.508,24.766-41.93
c58.742-32.406,95.234-94.156,95.234-161.156C440.006,133.43,418.858,84.547,381.983,49.895z"/>
</g>
<g>
<circle style="fill:#FFE671;" cx="256.002" cy="184.008" r="128"/>
</g>
<g>
<g>
<path style="fill:#FFFFFF;" d="M283.986,232.008c-10.633,0-16.07-6.203-20.031-10.727c-3.492-3.984-4.883-5.273-8-5.273
c-3.102,0-4.484,1.289-7.961,5.266c-3.734,4.273-9.383,10.734-20.016,10.734c-10.625,0-16.273-6.461-20.008-10.734
c-3.477-3.977-4.859-5.266-7.961-5.266c-4.422,0-8-3.578-8-8s3.578-8,8-8c10.625,0,16.273,6.461,20.008,10.734
c3.477,3.977,4.859,5.266,7.961,5.266s4.492-1.289,7.969-5.266c3.734-4.273,9.383-10.734,20.008-10.734
c10.633,0,16.07,6.203,20.031,10.727c3.492,3.984,4.883,5.273,8,5.273c3.109,0,4.5-1.289,7.992-5.266
c3.742-4.273,9.391-10.734,20.031-10.734c4.422,0,8,3.578,8,8s-3.578,8-8,8c-3.117,0-4.508,1.289-8,5.273
C300.267,225.555,294.619,232.008,283.986,232.008z"/>
</g>
</g>
<g>
<g>
<path style="fill:#5C546A;" d="M232.002,384.008c-3.68,0-6.992-2.555-7.813-6.297l-40-184c-0.938-4.32,1.805-8.578,6.117-9.523
c4.344-0.953,8.578,1.805,9.523,6.117l40,184c0.938,4.32-1.805,8.578-6.117,9.523
C233.135,383.954,232.564,384.008,232.002,384.008z"/>
</g>
</g>
<g>
<g>
<path style="fill:#5C546A;" d="M280.017,384.008c-0.563,0-1.133-0.055-1.711-0.18c-4.313-0.945-7.055-5.203-6.117-9.523l40-184
c0.945-4.313,5.164-7.063,9.523-6.117c4.313,0.945,7.055,5.203,6.117,9.523l-40,184
C287.01,381.454,283.697,384.008,280.017,384.008z"/>
</g>
</g>
<g>
<path style="fill:#B4B6BC;" d="M190.633,376.008c0.851,3.593,1.374,7.293,1.374,11.086v60.914c0,17.648,14.352,32,32,32h64
c17.648,0,32-14.352,32-32v-60.914c0-3.79,0.527-7.491,1.384-11.086H190.633z"/>
</g>
<g>
<path style="fill:#868491;" d="M256.002,376.008h-65.369c0.851,3.593,1.374,7.293,1.374,11.086v60.914c0,17.648,14.352,32,32,32
h31.995V376.008z"/>
</g>
<g>
<g>
<rect x="256.002" y="400.008" style="fill:#868491;" width="64" height="16"/>
</g>
</g>
<g>
<g>
<rect x="256.006" y="432.008" style="fill:#868491;" width="64" height="16"/>
</g>
</g>
<g>
<g>
<rect x="192.002" y="400.008" style="fill:#5C546A;" width="64" height="16"/>
</g>
</g>
<g>
<g>
<rect x="192.002" y="432.008" style="fill:#5C546A;" width="64" height="16"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -7,18 +7,23 @@ import { debug,
DebugConfigurationProviderTriggerKind,
DebugSessionCustomEvent,
Disposable,
ProviderResult
workspace,
DebugSession
} from "vscode";
import * as path from "path";
import psList from 'ps-list';
import { ListenablePreferenceSetting } from "@pivotal-tools/commons-vscode/lib/launch-util";
import { getPortPromise } from "portfinder";
import { RemoteBootApp } from "./live-hover-connect-ui";
const JMX_VM_ARG = '-Dspring.jmx.enabled='
const ACTUATOR_JMX_EXPOSURE_ARG = '-Dmanagement.endpoints.jmx.exposure.include='
const ADMIN_VM_ARG = '-Dspring.application.admin.enabled='
const JMX_VM_ARG = '-Dspring.jmx.enabled=';
const ACTUATOR_JMX_EXPOSURE_ARG = '-Dmanagement.endpoints.jmx.exposure.include=';
const ADMIN_VM_ARG = '-Dspring.application.admin.enabled=';
const BOOT_PROJECT_ARG = '-Dspring.boot.project.name=';
const RMI_HOSTNAME = '-Djava.rmi.server.hostname=localhost';
const JMX_PORT_ARG = '-Dcom.sun.management.jmxremote.port=';
export const TEST_RUNNER_MAIN_CLASSES = [
'org.eclipse.jdt.internal.junit.runner.RemoteTestRunner',
'com.microsoft.java.test.runner.Launcher'
@@ -30,12 +35,13 @@ interface ProcessEvent {
shellProcessId: number
}
class SpringBootDebugConfigProvider implements DebugConfigurationProvider {
resolveDebugConfigurationWithSubstitutedVariables(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult<DebugConfiguration> {
async resolveDebugConfigurationWithSubstitutedVariables(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken) {
// Running app live hovers support
if (!TEST_RUNNER_MAIN_CLASSES.includes(debugConfiguration.mainClass) && isActuatorOnClasspath(debugConfiguration)) {
if (debugConfiguration.vmArgs) {
if (typeof debugConfiguration.vmArgs === 'string') {
if (debugConfiguration.vmArgs.indexOf(JMX_VM_ARG) < 0) {
debugConfiguration.vmArgs += ` ${JMX_VM_ARG}true`;
}
@@ -51,8 +57,15 @@ class SpringBootDebugConfigProvider implements DebugConfigurationProvider {
if (debugConfiguration.vmArgs.indexOf(RMI_HOSTNAME) < 0) {
debugConfiguration.vmArgs += ` ${RMI_HOSTNAME}`;
}
if (debugConfiguration.vmArgs.indexOf(JMX_PORT_ARG) < 0) {
debugConfiguration.vmArgs += ` ${JMX_PORT_ARG}${await getPortPromise({
startPort: 10000
})} -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false`
}
} else {
debugConfiguration.vmArgs = `${JMX_VM_ARG}true ${ACTUATOR_JMX_EXPOSURE_ARG}* ${ADMIN_VM_ARG}true ${BOOT_PROJECT_ARG}${debugConfiguration.projectName} ${RMI_HOSTNAME}`;
debugConfiguration.vmArgs = `${JMX_VM_ARG}true ${ACTUATOR_JMX_EXPOSURE_ARG}* ${ADMIN_VM_ARG}true ${BOOT_PROJECT_ARG}${debugConfiguration.projectName} ${RMI_HOSTNAME} ${JMX_PORT_ARG}${await getPortPromise({
startPort: 10000
})} -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false`;
}
}
return debugConfiguration;
@@ -87,12 +100,13 @@ export function hookListenerToBooleanPreference(setting: string, listenerCreator
}
export function startDebugSupport(): Disposable {
return hookListenerToBooleanPreference(
'boot-java.live-information.automatic-connection.on',
() => Disposable.from(
debug.onDidReceiveDebugSessionCustomEvent(handleCustomDebugEvent),
debug.registerDebugConfigurationProvider('java', new SpringBootDebugConfigProvider(), DebugConfigurationProviderTriggerKind.Initial)
)
return Disposable.from(
hookListenerToBooleanPreference(
'boot-java.live-information.automatic-connection.on',
() => debug.registerDebugConfigurationProvider('java', new SpringBootDebugConfigProvider(), DebugConfigurationProviderTriggerKind.Initial)
),
debug.onDidReceiveDebugSessionCustomEvent(handleCustomDebugEvent),
debug.onDidTerminateDebugSession(handleTerminateDebugSession)
);
}
@@ -102,13 +116,34 @@ async function handleCustomDebugEvent(e: DebugSessionCustomEvent): Promise<void>
if (canConnect(debugConfiguration)) {
setTimeout(async () => {
const pid = await getAppPid(e.body as ProcessEvent);
const processKey = pid.toString();
commands.executeCommand('sts/livedata/connect', { processKey });
const vmArgs = debugConfiguration.vmArgs as string;
let idx = vmArgs.indexOf(JMX_PORT_ARG) + JMX_PORT_ARG.length;
let jmxPort = "";
for (; idx < vmArgs.length && vmArgs.charAt(idx) <= "9" && vmArgs.charAt(idx) >= "0"; idx++) {
jmxPort += vmArgs.charAt(idx);
}
await commands.executeCommand("vscode-spring-boot.live.activate", {
host: "127.0.0.1",
port: null,
urlScheme: "http",
jmxurl: `service:jmx:rmi:///jndi/rmi://127.0.0.1:${jmxPort}/jmxrmi`,
manualConnect: true,
processId: pid.toString(),
processName: debugConfiguration.mainClass,
projectName: debugConfiguration.projectName
} as RemoteBootApp);
if (workspace.getConfiguration("boot-java.live-information.automatic-connection").get("on")) {
await commands.executeCommand("vscode-spring-boot.live.show.active");
}
}, 500);
}
}
}
function handleTerminateDebugSession(session: DebugSession) {
commands.executeCommand('vscode-spring-boot.live.deactivate');
}
async function getAppPid(e: ProcessEvent): Promise<number> {
if (e.processId && e.processId > 0) {
return e.processId;
@@ -141,8 +176,9 @@ function isActuatorJarFile(f: string): boolean {
function canConnect(debugConfiguration: DebugConfiguration): boolean {
if (!TEST_RUNNER_MAIN_CLASSES.includes(debugConfiguration.mainClass) && isActuatorOnClasspath(debugConfiguration)) {
return debugConfiguration.vmArgs
return typeof debugConfiguration.vmArgs === 'string'
&& debugConfiguration.vmArgs.indexOf(`${JMX_VM_ARG}true`) >= 0
&& debugConfiguration.vmArgs.indexOf(JMX_PORT_ARG) >= 0
&& debugConfiguration.vmArgs.indexOf(`${ADMIN_VM_ARG}true`) >= 0
}
return false;

View File

@@ -1,8 +1,7 @@
'use strict';
import * as VSCode from 'vscode';
import { LanguageClient } from "vscode-languageclient/node";
import { ActivatorOptions } from '@pivotal-tools/commons-vscode';
import { commands, window } from 'vscode';
interface ProcessCommandInfo {
processKey : string;
@@ -11,30 +10,74 @@ interface ProcessCommandInfo {
projectName: string;
}
export interface RemoteBootApp {
jmxurl: string;
host: string;
urlScheme: "https" | "http";
port: string;
manualConnect: boolean;
keepChecking?: boolean;
processId: string;
processName: string;
projectName?: string;
}
type BootAppState = "none" | "connecting" | "connected" | "disconnecting" | "disconnected";
let activeBootApp: RemoteBootApp | undefined;
let state: BootAppState
async function liveHoverConnectHandler() {
//sts.vscode-spring-boot.codeAction
const processData : ProcessCommandInfo[] = await VSCode.commands.executeCommand('sts/livedata/listProcesses');
const choiceMap = new Map<string, ProcessCommandInfo>();
const choices : string[] = [];
processData.forEach(p => {
const slash = p.action.lastIndexOf('/');
if (slash>=0) {
var actionLabel = p.action.substring(slash+1);
actionLabel = actionLabel.substring(0, 1).toUpperCase() + actionLabel.substring(1);
const choiceLabel = actionLabel + " " + p.label;
choiceMap.set(choiceLabel, p);
choices.push(choiceLabel);
let actionLabel = "";
switch (p.action) {
case "sts/livedata/connect":
actionLabel = "Show"
break;
case "sts/livedata/refresh":
actionLabel = "Refresh";
break;
case "sts/livedata/disconnect":
actionLabel = "Hide";
break;
}
const choiceLabel = actionLabel + " Live Data from: " + p.label;
choiceMap.set(choiceLabel, p);
choices.push(choiceLabel);
});
if (choices) {
const picked = await VSCode.window.showQuickPick(choices);
if (picked) {
const chosen = choiceMap.get(picked);
await VSCode.commands.executeCommand(chosen.action, chosen);
if (activeBootApp?.jmxurl === chosen.processKey) {
switch (chosen.action) {
case "sts/livedata/connect":
await commands.executeCommand('vscode-spring-boot.live.show.active');
break;
case "sts/livedata/disconnect":
await commands.executeCommand('vscode-spring-boot.live.hide.active');
break;
default:
await VSCode.commands.executeCommand(chosen.action, chosen);
}
} else {
await VSCode.commands.executeCommand(chosen.action, chosen);
}
}
}
}
async function updateBootAppState(newState: BootAppState) {
if (newState !== state) {
state = newState;
commands.executeCommand('setContext', 'vscode-spring-boot.active-app-state', state);
}
}
/** Called when extension is activated */
export function activate(
client: LanguageClient,
@@ -42,12 +85,58 @@ export function activate(
context: VSCode.ExtensionContext
) {
context.subscriptions.push(
VSCode.commands.registerCommand('vscode-spring-boot.live-hover.connect', () => {
commands.registerCommand('vscode-spring-boot.live-hover.connect', () => {
if (client.isRunning()) {
return liveHoverConnectHandler();
} else {
VSCode.window.showErrorMessage("No Spring Boot project found. Action is only available for Spring Boot Projects");
window.showErrorMessage("No Spring Boot project found. Action is only available for Spring Boot Projects");
}
})
}),
commands.registerCommand("vscode-spring-boot.live.activate", async appData => {
activeBootApp = appData;
await commands.executeCommand('sts/livedata/localAdd', activeBootApp);
updateBootAppState("disconnected");
}),
commands.registerCommand("vscode-spring-boot.live.deactivate", async () => {
await commands.executeCommand('sts/livedata/localRemove', activeBootApp.jmxurl);
activeBootApp = undefined;
updateBootAppState("none");
}),
commands.registerCommand("vscode-spring-boot.live.show.active", async () => {
try {
updateBootAppState("connecting");
await commands.executeCommand('sts/livedata/connect', {
processKey: activeBootApp.jmxurl
});
updateBootAppState("connected");
} catch (error) {
updateBootAppState("disconnected");
throw error;
}
}),
commands.registerCommand("vscode-spring-boot.live.refresh.active", async () => {
await commands.executeCommand('sts/livedata/refresh', {
processKey: activeBootApp.jmxurl
});
}),
commands.registerCommand("vscode-spring-boot.live.hide.active", async () => {
try {
updateBootAppState("disconnecting");
await commands.executeCommand('sts/livedata/disconnect', {
processKey: activeBootApp.jmxurl
});
updateBootAppState("disconnected");
} catch (error) {
updateBootAppState("connected");
throw error;
}
}),
);
}

View File

@@ -124,13 +124,42 @@
{
"command": "vscode-spring-boot.yaml-to-props",
"when": "false"
},
{
"command": "vscode-spring-boot.live.show.active",
"when": "false"
},
{
"command": "vscode-spring-boot.live.hide.active",
"when": "false"
},
{
"command": "vscode-spring-boot.live.refresh.active",
"when": "false"
}
],
"debug/toolBar": [
{
"when": "inDebugMode && debugType == 'java' && (vscode-spring-boot.active-app-state == 'disconnected' || vscode-spring-boot.active-app-state == 'connecting')",
"command": "vscode-spring-boot.live.show.active",
"group": "Spring Boot"
},
{
"when": "inDebugMode && debugType == 'java' && vscode-spring-boot.active-app-state == 'connected'",
"command": "vscode-spring-boot.live.refresh.active",
"group": "Spring Boot"
},
{
"when": "inDebugMode && debugType == 'java' && (vscode-spring-boot.active-app-state == 'connected' || vscode-spring-boot.active-app-state == 'disconnecting')",
"command": "vscode-spring-boot.live.hide.active",
"group": "Spring Boot"
}
]
},
"commands": [
{
"command": "vscode-spring-boot.live-hover.connect",
"title": "Manage Live Spring Boot Process Connections",
"title": "Show/Refresh/Hide Live Data from Spring Boot Processes",
"category": "Spring Boot"
},
{
@@ -180,6 +209,27 @@
"command": "vscode-spring-boot.yaml-to-props",
"title": "Convert .yaml to .properties",
"category": "Spring Boot"
},
{
"command": "vscode-spring-boot.live.show.active",
"title": "Show Live Data from launched Spring Boot App",
"enablement": "vscode-spring-boot.active-app-state == 'disconnected'",
"icon": "./images/light-bulb-off.svg",
"category": "Spring Boot"
},
{
"command": "vscode-spring-boot.live.hide.active",
"title": "Hide Live Data from launched Spring Boot App",
"enablement": "vscode-spring-boot.active-app-state == 'connected'",
"icon": "./images/light-bulb-on.svg",
"category": "Spring Boot"
},
{
"command": "vscode-spring-boot.live.refresh.active",
"title": "Refresh Live Data from launched Spring Boot App",
"enablement": "vscode-spring-boot.active-app-state == 'connected'",
"icon": "$(refresh)",
"category": "Spring Boot"
}
],
"configuration": [
@@ -198,6 +248,11 @@
"default": true,
"description": "Live Information - Automatic addition of JVM arguments enabling JMX and Process Connection via JMX Enabled"
},
"boot-java.live-information.all-local-java-processes": {
"type": "boolean",
"description": "Show all local JVM processes when connecting to Live Spring Boot App processes",
"default": false
},
"boot-java.live-information.fetch-data.max-retries": {
"type": "number",
"default": 10,
@@ -1099,6 +1154,7 @@
"vsce-pre-release-package": "vsce package --pre-release"
},
"dependencies": {
"portfinder": "1.0.32",
"@pivotal-tools/commons-vscode": "file:../commons-vscode/pivotal-tools-commons-vscode-0.2.4.tgz",
"ps-list": "^7.2.0",
"vscode-languageclient": "^9.0.1"