diff --git a/CHANGELOG.md b/CHANGELOG.md index d215aa2..8c598b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ ## Unreleased +## [4.4.0] 2025-02-07 + - Add `timeout` and `killSignal` run options +- Add `windowsHide` (default: true) to allow to show window if `windowHide: true` is sent ## [4.3.3] 2025-02-03 diff --git a/README.md b/README.md index 9aa8c8c..f42d95f 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ Example: `["-Xms256m", "--someflagwithvalue myVal", "-c"]` | javaArgs | List of arguments for JVM only, not the JAR or the class | `[]` | `['--add-opens=java.base/java.lang=ALL-UNNAMED']` | | [windowsVerbatimArguments](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) | No quoting or escaping of arguments is done on Windows. Ignored on Unix. This is set to true automatically when shell is specified and is CMD. | `true` | `false` | | [windowless](https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#:~:text=main()%20method.-,javaw,information%20if%20a%20launch%20fails.) | If windowless is true, JavaCaller calls javaw instead of java to not create any windows, useful when using detached on Windows. Ignored on Unix. | false | true +| [windowsHide](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) | On Windows, hide the subprocess console window that would normally be created. Set to `false` if you need Java UI dialogs to be visible (e.g., print dialogs). Ignored on Unix. | `true` | `false` | [timeout](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) | In milliseconds the maximum amount of time the process is allowed to run. | `undefined` | `1000` | [killSignal](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) | The signal value to be used when the spawned process will be killed by timeout or abort signal. | `SIGTERM` | `SIGINT` @@ -149,6 +150,23 @@ const java = new JavaCaller({ const { status, stdout, stderr } = await java.run(['--sleep'], { windowless: true }); ``` +Call java process with visible windows (e.g., for print dialogs) + +```javascript +const java = new JavaCaller({ + classPath: 'test/java/dist', + mainClass: 'com.nvuillam.javacaller.JavaCallerTester' +}); +// Set windowsHide to false to allow Java UI dialogs to be visible +const { status, stdout, stderr } = await java.run([], { windowsHide: false }); +``` + +When using CLI mode with `--no-windows-hide` flag: + +```bash +node index.js --no-windows-hide +``` + You can see **more examples in** [**test methods**](https://github.com/nvuillam/node-java-caller/blob/master/test/java-caller.test.js) ## TROUBLESHOOTING diff --git a/lib/cli.js b/lib/cli.js index 36ba000..6e85230 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -26,7 +26,18 @@ class JavaCallerCli { const java = new JavaCaller(this.javaCallerOptions); const args = [...process.argv]; args.splice(0, 2); - const { status } = await java.run(args); + + // Parse --no-windows-hide flag (remove all occurrences) + const runOptions = {}; + const filteredArgs = args.filter(arg => { + if (arg === '--no-windows-hide') { + runOptions.windowsHide = false; + return false; // Remove from args + } + return true; // Keep in args + }); + + const { status } = await java.run(filteredArgs, runOptions); process.exitCode = status; } } diff --git a/lib/index.d.ts b/lib/index.d.ts index 5d7714c..b57ee59 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -131,6 +131,13 @@ export interface JavaCallerRunOptions { * @default "SIGTERM" */ killSignal?: number | NodeJS.Signals; + + /** + * On Windows, hide the subprocess console window that would normally be created. + * This option is ignored on Unix. + * @default true + */ + windowsHide?: boolean; } /** diff --git a/lib/java-caller.js b/lib/java-caller.js index 1545ee6..97f79eb 100644 --- a/lib/java-caller.js +++ b/lib/java-caller.js @@ -115,7 +115,7 @@ class JavaCaller { cwd: javaExeToUse === "java" || javaExeToUse === "javaw" ? runOptions.cwd : undefined, env: Object.assign({}, process.env), stdio: this.output === "console" ? "inherit" : runOptions.detached ? "ignore" : "pipe", - windowsHide: true, + windowsHide: runOptions.windowsHide !== undefined ? runOptions.windowsHide : true, windowsVerbatimArguments: runOptions.windowsVerbatimArguments, }; if (javaExeToUse.includes(" ")) { diff --git a/test/java-caller.test.js b/test/java-caller.test.js index a1229a0..6a90e49 100644 --- a/test/java-caller.test.js +++ b/test/java-caller.test.js @@ -58,6 +58,36 @@ describe("Call with classes", () => { checkStatus(0, status, stdout, stderr); }); + it("should call JavaCallerTester.class with windowsHide set to false", async () => { + const java = new JavaCaller({ + classPath: 'test/java/dist', + mainClass: 'com.nvuillam.javacaller.JavaCallerTester' + }); + const { status, stdout, stderr } = await java.run([], { windowsHide: false }); + checkStatus(0, status, stdout, stderr); + checkStdOutIncludes(`JavaCallerTester is called !`, stdout, stderr); + }); + + it("should call JavaCallerTester.class with windowsHide explicitly set to true", async () => { + const java = new JavaCaller({ + classPath: 'test/java/dist', + mainClass: 'com.nvuillam.javacaller.JavaCallerTester' + }); + const { status, stdout, stderr } = await java.run([], { windowsHide: true }); + checkStatus(0, status, stdout, stderr); + checkStdOutIncludes(`JavaCallerTester is called !`, stdout, stderr); + }); + + it("should call JavaCallerTester.class with default windowsHide (should be true)", async () => { + const java = new JavaCaller({ + classPath: 'test/java/dist', + mainClass: 'com.nvuillam.javacaller.JavaCallerTester' + }); + const { status, stdout, stderr } = await java.run([]); + checkStatus(0, status, stdout, stderr); + checkStdOutIncludes(`JavaCallerTester is called !`, stdout, stderr); + }); + it("should call JavaCallerTester.class with proper stdout encoding", async () => { const java = new JavaCaller({ @@ -188,6 +218,23 @@ describe("Call with classes", () => { await javaCli.process(); }); + it("should use JavaCallerCli with --no-windows-hide flag", async () => { + // Save original argv + const originalArgv = process.argv; + + // Mock process.argv to include --no-windows-hide + process.argv = ['node', 'script.js', '--no-windows-hide']; + + try { + const javaCli = new JavaCallerCli("examples/cli_app/lib"); + await javaCli.process(); + checkStatus(0, process.exitCode); + } finally { + // Restore original argv + process.argv = originalArgv; + } + }); + it("Should work with an absolute path", async () => { const absolutePath = path.join(process.cwd(), "test/java/jar/JavaCallerTesterRunnable.jar");