From 3fdde0a0bb75ed671b9a275196de532a3f617362 Mon Sep 17 00:00:00 2001 From: Riccardo Strina Date: Sun, 26 Jul 2026 12:34:01 +0200 Subject: [PATCH] fix(tasks): Launch Maven mains in standalone JVM Use exec:exec with an explicit Java executable so applications no longer run inside Maven's JVM. Preserve classpath scope and support inherited I/O and long classpaths. Closes #284 --- languages/java/tasks.json | 2 +- tests/task_verification_test.rs | 34 +++++++++++++++++++++++++-------- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/languages/java/tasks.json b/languages/java/tasks.json index c1d828b..6c8d307 100644 --- a/languages/java/tasks.json +++ b/languages/java/tasks.json @@ -1,7 +1,7 @@ [ { "label": "Run $ZED_CUSTOM_java_class_name", - "command": "pkg=\"${ZED_CUSTOM_java_package_name:-}\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; case \"$f\" in *\"/src/test/\"*) COMPILE_GOAL=\"test-compile\"; CLASSPATH_SCOPE=\"test\";; *) COMPILE_GOAL=\"compile\"; CLASSPATH_SCOPE=\"runtime\";; esac; if [ \"$m\" = \".\" ]; then $CMD clean $COMPILE_GOAL exec:java -Dexec.mainClass=\"$c\" -Dexec.classpathScope=$CLASSPATH_SCOPE; else $CMD clean $COMPILE_GOAL -pl \"$m\" -am && $CMD exec:java -pl \"$m\" -Dexec.mainClass=\"$c\" -Dexec.classpathScope=$CLASSPATH_SCOPE; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;", + "command": "pkg=\"${ZED_CUSTOM_java_package_name:-}\"; cls=\"$ZED_CUSTOM_java_class_name\"; if [ -n \"$pkg\" ]; then c=\"$pkg.$cls\"; else c=\"$cls\"; fi; f=\"$ZED_FILE\"; p=\"$PWD\"; d=$(dirname \"${f#$p/}\"); if [ -f pom.xml ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/pom.xml\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; [ -f ./mvnw ] && CMD=\"./mvnw\" || CMD=\"mvn\"; case \"$f\" in *\"/src/test/\"*) COMPILE_GOAL=\"test-compile\"; CLASSPATH_SCOPE=\"test\";; *) COMPILE_GOAL=\"compile\"; CLASSPATH_SCOPE=\"runtime\";; esac; if [ \"$m\" = \".\" ]; then $CMD clean $COMPILE_GOAL exec:exec -Dexec.executable=java -Dexec.args=\"-classpath %classpath $c\" -Dexec.classpathScope=$CLASSPATH_SCOPE -Dexec.inheritIo=true -Dexec.longClasspath=true; else $CMD clean $COMPILE_GOAL -pl \"$m\" -am && $CMD exec:exec -pl \"$m\" -Dexec.executable=java -Dexec.args=\"-classpath %classpath $c\" -Dexec.classpathScope=$CLASSPATH_SCOPE -Dexec.inheritIo=true -Dexec.longClasspath=true; fi; elif [ -f build.gradle ] || [ -f build.gradle.kts ] || [ -f settings.gradle ] || [ -f settings.gradle.kts ]; then m=\".\"; md=\"$d\"; while [ \"$md\" != \".\" ] && [ \"$md\" != \"/\" ]; do if [ -f \"$md/build.gradle\" ] || [ -f \"$md/build.gradle.kts\" ]; then m=\"$md\"; break; fi; md=$(dirname \"$md\"); done; if [ \"$m\" = \".\" ]; then mp=\"\"; else mp=\":$(echo \"$m\" | tr '/' ':')\"; fi; [ -f ./gradlew ] && CMD=\"./gradlew\" || CMD=\"gradle\"; $CMD ${mp}:run -PmainClass=\"$c\"; else find . -name '*.java' -not -path './bin/*' -not -path './target/*' -not -path './build/*' -print0 | xargs -0 javac -d bin && java -cp bin \"$c\"; fi;", "use_new_terminal": false, "reveal": "always", "tags": [ diff --git a/tests/task_verification_test.rs b/tests/task_verification_test.rs index 20f1a40..3588ceb 100644 --- a/tests/task_verification_test.rs +++ b/tests/task_verification_test.rs @@ -201,18 +201,26 @@ fn test_maven_single_module_command_logic() { .run(); assert!( - stdout.contains("MVN_CALLED: clean compile exec:java -Dexec.mainClass=com.example.Main"), + stdout.contains("MVN_CALLED: clean compile exec:exec"), "Should run as single module. Got: {}", stdout ); + assert!( + stdout.contains("-Dexec.executable=java") + && stdout.contains("-Dexec.args=-classpath %classpath com.example.Main") + && stdout.contains("-Dexec.inheritIo=true") + && stdout.contains("-Dexec.longClasspath=true"), + "Should launch the main class in a standalone JVM. Got: {}", + stdout + ); assert!( stdout.contains("-Dexec.classpathScope=runtime"), "Should use runtime classpath scope. Got: {}", stdout ); assert!( - stdout_test - .contains("MVN_CALLED: clean test-compile exec:java -Dexec.mainClass=com.example.Main"), + stdout_test.contains("MVN_CALLED: clean test-compile exec:exec") + && stdout_test.contains("-Dexec.args=-classpath %classpath com.example.Main"), "Should run as single module. Got: {}", stdout_test ); @@ -239,10 +247,15 @@ fn test_maven_multi_module_command_logic() { stdout ); assert!( - stdout.contains("MVN_CALLED: exec:java -pl module-a"), + stdout.contains("MVN_CALLED: exec:exec -pl module-a"), "Should run only the submodule. Got: {}", stdout ); + assert!( + stdout.contains("-Dexec.args=-classpath %classpath com.example.Main"), + "Should launch the submodule main class in a standalone JVM. Got: {}", + stdout + ); assert!( stdout.contains("-Dexec.classpathScope=runtime"), "Should use runtime classpath scope. Got: {}", @@ -255,10 +268,15 @@ fn test_maven_multi_module_command_logic() { stdout_test ); assert!( - stdout_test.contains("MVN_CALLED: exec:java -pl module-a"), + stdout_test.contains("MVN_CALLED: exec:exec -pl module-a"), "Should run only the submodule. Got: {}", stdout_test ); + assert!( + stdout_test.contains("-Dexec.args=-classpath %classpath com.example.Main"), + "Should launch the submodule test class in a standalone JVM. Got: {}", + stdout_test + ); assert!( stdout_test.contains("-Dexec.classpathScope=test"), "Should use test classpath scope. Got: {}", @@ -281,7 +299,7 @@ fn test_maven_nested_module_command_logic() { stdout ); assert!( - stdout.contains("MVN_CALLED: exec:java -pl nested/module-b"), + stdout.contains("MVN_CALLED: exec:exec -pl nested/module-b"), "Should run only the nested submodule. Got: {}", stdout ); @@ -372,7 +390,7 @@ fn test_maven_single_level_package_logic() { .run(); assert!( - stdout.contains("-Dexec.mainClass=example.Main"), + stdout.contains("-Dexec.args=-classpath %classpath example.Main"), "Should include the single-level package in Maven. Got: {}", stdout ); @@ -384,7 +402,7 @@ fn test_maven_default_package_command_logic() { let stdout = project.task("java-main").package("").class("Main").run(); assert!( - stdout.contains("-Dexec.mainClass=Main"), + stdout.contains("-Dexec.args=-classpath %classpath Main"), "Should not include leading dot for default package in Maven. Got: {}", stdout );