feat(run): Tomcat local run configuration (#392)

Adds Tomcat Server › Local run configurations for WAR applications: installation validation and version detection, Maven/Gradle WAR artifact discovery and build before launch, WAR file or exploded-directory deployment, context path and HTTP port, project JDK or a chosen JRE, VM options, environment variables and opening the browser.

Each configuration runs with its own CATALINA_BASE in the project cache (HTTP, shutdown and local JMX ports configured there); Stop requests a clean shutdown before the usual process timeout. Debug attaches the Java debugger over JDWP. Update Application (Cmd+F10) updates resources, classes and resources with HotSwap, redeploys or restarts the server. Configurations are grouped under Tomcat Server in the configuration tree and Services.

Not verified on a real Windows machine; command construction is unit tested.

Fixes #205
Цей коміт міститься в:
l0ng-ai authored and GitHub committed 2026-10-30 18:06:11 +08:00
1 джерело d837cb1acb
коміт 92b3ae508c
32 files changed
+1366 -14

No files matched your search

+10
Переглянути файл
@@ -87,6 +87,16 @@ describe("HotSwap", () => {
expect(notes.at(-1)).toMatchObject({ level: "info", title: "2 classes reloaded" });
});
it("limits a configuration update to that configuration's debug session", async () => {
const one = { ...session(1, true), runConfig: { id: "tomcat:one" } as NonNullable<DebugSession["runConfig"]> };
const two = { ...session(2, true), runConfig: { id: "tomcat:two" } as NonNullable<DebugSession["runConfig"]> };
useDebug.setState({ sessions: { 1: one, 2: two } });
await reloadChangedClasses(false, "tomcat:two");
expect(ipc.javaBuildWorkspace).not.toHaveBeenCalled();
expect(ipc.debugRedefineClasses).toHaveBeenCalledTimes(1);
expect(ipc.debugRedefineClasses).toHaveBeenCalledWith(2);
});
it("does not reload after a failed build", async () => {
ipc.javaBuildWorkspace.mockImplementationOnce(() => ipc.ok("failed"));
await reloadChangedClasses();
+7 -1
Переглянути файл
@@ -341,7 +341,13 @@ export function registerDebugActions(): () => void {
icon: "refresh",
keywords: ["hotswap", "hot swap", "hot code replace"],
when: "debugCanHotSwap",
run: () => reloadChangedClasses(),
run: ({ arg }) =>
reloadChangedClasses(
true,
typeof arg === "object" && arg !== null && "configId" in arg && typeof arg.configId === "string"
? arg.configId
: undefined,
),
},
{
id: "debug.rerun",
+2 -2
Переглянути файл
@@ -33,8 +33,8 @@ export function hotSwapMessage(
}
/** Compiles (unless `build` is false) and reloads changed classes into every Java debug session. */
export async function reloadChangedClasses(build = true): Promise<void> {
const sessions = hotSwapSessions();
export async function reloadChangedClasses(build = true, configId?: string): Promise<void> {
const sessions = hotSwapSessions().filter((s) => !configId || s.runConfig?.id === configId);
if (!sessions.length || !isTauri()) {
notifications().notify({ group: "run", level: "info", key: KEY, title: "No debug session can reload classes" });
return;