Skip to content

Commit 4f2bc3a

Browse files
committed
add forgotten files
1 parent 1b586c8 commit 4f2bc3a

File tree

3 files changed

+300
-0
lines changed

3 files changed

+300
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import java.io.IOException;
25+
import java.io.UncheckedIOException;
26+
import java.nio.file.Files;
27+
import java.nio.file.Path;
28+
import java.nio.file.StandardOpenOption;
29+
import java.text.SimpleDateFormat;
30+
import java.util.Date;
31+
import java.util.List;
32+
33+
public class UseShutdownHook {
34+
35+
public static void main(String[] args) throws InterruptedException {
36+
trace("Started");
37+
38+
var outputFile = Path.of(args[0]);
39+
trace(String.format("Write output in [%s] file", outputFile));
40+
41+
var shutdownTimeoutSeconds = Integer.parseInt(args[1]);
42+
trace(String.format("Automatically shutdown the app in %ss", shutdownTimeoutSeconds));
43+
44+
Runtime.getRuntime().addShutdownHook(new Thread() {
45+
@Override
46+
public void run() {
47+
output(outputFile, "shutdown hook executed");
48+
}
49+
});
50+
51+
var startTime = System.currentTimeMillis();
52+
var lock = new Object();
53+
do {
54+
synchronized (lock) {
55+
lock.wait(shutdownTimeoutSeconds * 1000);
56+
}
57+
} while ((System.currentTimeMillis() - startTime) < (shutdownTimeoutSeconds * 1000));
58+
59+
output(outputFile, "exit");
60+
}
61+
62+
private static void output(Path outputFilePath, String msg) {
63+
64+
trace(String.format("Writing [%s] into [%s]", msg, outputFilePath));
65+
66+
try {
67+
Files.createDirectories(outputFilePath.getParent());
68+
Files.writeString(outputFilePath, msg, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
69+
} catch (IOException ex) {
70+
throw new UncheckedIOException(ex);
71+
}
72+
}
73+
74+
private static void trace(String msg) {
75+
Date time = new Date(System.currentTimeMillis());
76+
msg = String.format("UseShutdownHook [%s]: %s", SDF.format(time), msg);
77+
System.out.println(msg);
78+
try {
79+
Files.write(traceFile, List.of(msg), StandardOpenOption.APPEND, StandardOpenOption.CREATE);
80+
} catch (IOException ex) {
81+
throw new UncheckedIOException(ex);
82+
}
83+
}
84+
85+
private static final SimpleDateFormat SDF = new SimpleDateFormat("HH:mm:ss.SSS");
86+
87+
private static final Path traceFile = Path.of(System.getProperty("jpackage.test.trace-file"));
88+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#
2+
# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
#
5+
# This code is free software; you can redistribute it and/or modify it
6+
# under the terms of the GNU General Public License version 2 only, as
7+
# published by the Free Software Foundation.
8+
#
9+
# This code is distributed in the hope that it will be useful, but WITHOUT
10+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
# version 2 for more details (a copy is included in the LICENSE file that
13+
# accompanied this code).
14+
#
15+
# You should have received a copy of the GNU General Public License version
16+
# 2 along with this work; if not, write to the Free Software Foundation,
17+
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
#
19+
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
# or visit www.oracle.com if you need additional information or have any
21+
# questions.
22+
#
23+
24+
param (
25+
# Path to executable to start.
26+
[Parameter(Mandatory=$true)]
27+
[string]$Executable,
28+
29+
# Timeout to wait after the executable has been started.
30+
[Parameter(Mandatory=$true)]
31+
[double]$TimeoutSeconds
32+
)
33+
34+
$type = @{
35+
TypeDefinition = @'
36+
using System;
37+
using System.Runtime.InteropServices;
38+
39+
namespace Stuff {
40+
41+
internal struct Details {
42+
[DllImport("kernel32.dll", SetLastError = true)]
43+
[return: MarshalAs(UnmanagedType.Bool)]
44+
internal static extern bool GenerateConsoleCtrlEvent(uint dwCtrlEvent, uint dwProcessGroupId);
45+
}
46+
47+
public struct Facade {
48+
public static void GenerateConsoleCtrlEvent() {
49+
if (!Details.GenerateConsoleCtrlEvent(0, 0)) {
50+
reportLastErrorAndExit("GenerateConsoleCtrlEvent");
51+
}
52+
}
53+
54+
internal static void reportLastErrorAndExit(String func) {
55+
int errorCode = Marshal.GetLastWin32Error();
56+
Console.Error.WriteLine(func + " function failed with error code: " + errorCode);
57+
Environment.Exit(100);
58+
}
59+
}
60+
}
61+
'@
62+
}
63+
Add-Type @type
64+
65+
Set-PSDebug -Trace 2
66+
67+
# Launch the target executable.
68+
# `-NoNewWindow` parameter will attach the started process to the existing console.
69+
$childProc = Start-Process -PassThru -NoNewWindow $Executable
70+
71+
# Wait a bit to let the started process complete initialization.
72+
Start-Sleep -Seconds $TimeoutSeconds
73+
74+
# Call GenerateConsoleCtrlEvent to send a CTRL+C event to the launched executable.
75+
# CTRL+C event will be sent to all processes attached to the console of the current process,
76+
# i.e., it will be sent to this PowerShell process and to the started $Executable process because
77+
# it was configured to attach to the existing console (the console of this PowerShell process).
78+
[Stuff.Facade]::GenerateConsoleCtrlEvent()
79+
80+
# Wait for child process termination
81+
Wait-Process -InputObject $childProc
82+
83+
Exit 0
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
import static jdk.jpackage.test.HelloApp.configureAndExecute;
25+
26+
import java.io.IOException;
27+
import java.nio.file.Files;
28+
import java.nio.file.Path;
29+
import java.time.Duration;
30+
import jdk.jpackage.test.AdditionalLauncher;
31+
import jdk.jpackage.test.Annotations.Test;
32+
import jdk.jpackage.test.CfgFile;
33+
import jdk.jpackage.test.Executor;
34+
import jdk.jpackage.test.JPackageCommand;
35+
import jdk.jpackage.test.TKit;
36+
37+
/**
38+
* Test the child process has a chance to handle Ctrl+C signal.
39+
*/
40+
41+
/*
42+
* @test
43+
* @summary Test case for JDK-8365790
44+
* @library /test/jdk/tools/jpackage/helpers
45+
* @build jdk.jpackage.test.*
46+
* @build Win8365790Test
47+
* @requires (os.family == "windows")
48+
* @run main/othervm/timeout=100 -Xmx512m jdk.jpackage.test.Main
49+
* --jpt-run=Win8365790Test
50+
*/
51+
public class Win8365790Test {
52+
53+
@Test
54+
public void test() throws InterruptedException, IOException {
55+
56+
var outputDir = TKit.createTempDirectory("response-dir");
57+
58+
var mainOutputFile = outputDir.resolve("output.txt");
59+
var mainTraceFile = outputDir.resolve("trace.txt");
60+
61+
var probeOutputFile = outputDir.resolve("probe-output.txt");
62+
var probeTraceFile = outputDir.resolve("probe-trace.txt");
63+
64+
var cmd = JPackageCommand
65+
.helloAppImage(TEST_APP_JAVA + "*UseShutdownHook")
66+
.ignoreFakeRuntime()
67+
.addArguments("--java-options", "-Djpackage.test.trace-file=" + mainTraceFile.toString())
68+
.addArguments("--arguments", mainOutputFile.toString())
69+
.addArguments("--arguments", Long.toString(Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS).getSeconds()));
70+
71+
new AdditionalLauncher("probe") {
72+
@Override
73+
protected void verify(JPackageCommand cmd) {
74+
}
75+
}.addJavaOptions("-Djpackage.test.trace-file=" + probeTraceFile.toString())
76+
.addDefaultArguments(probeOutputFile.toString(), Long.toString(Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS).getSeconds()))
77+
.applyTo(cmd);
78+
79+
cmd.executeAndAssertImageCreated();
80+
81+
cmd.readLauncherCfgFile("probe")
82+
.add(new CfgFile().addValue("Application", "win.norestart", Boolean.TRUE.toString()))
83+
.save(cmd.appLauncherCfgPath("probe"));
84+
85+
// Try Ctrl+C signal on a launcher with disabled restart functionality.
86+
// It will create a single launcher process instead of the parent and the child processes.
87+
// Ctrl+C always worked for launcher with disabled restart functionality.
88+
var probeOutput = runLauncher(cmd, "probe", probeTraceFile, probeOutputFile);
89+
90+
if (!probeOutput.equals("shutdown hook executed")) {
91+
// Ctrl+C signal didn't make it. Test environment doesn't support Ctrl+C signal
92+
// delivery from the prowershell process to a child process, don't run the main
93+
// test.
94+
TKit.throwSkippedException(
95+
"The environment does NOT support Ctrl+C signal delivery from the prowershell process to a child process");
96+
}
97+
98+
var mainOutput = runLauncher(cmd, null, mainTraceFile, mainOutputFile);
99+
100+
TKit.assertEquals("shutdown hook executed", mainOutput, "Check shutdown hook executed");
101+
}
102+
103+
private static String runLauncher(JPackageCommand cmd, String launcherName, Path traceFile, Path outputFile) throws IOException {
104+
// Launch the specified launcher and send Ctrl+C signal to it.
105+
Thread.ofVirtual().start(() -> {
106+
configureAndExecute(0, Executor.of("powershell", "-NonInteractive", "-NoLogo", "-NoProfile", "-ExecutionPolicy", "Unrestricted")
107+
.addArgument("-File").addArgument(TEST_PS1)
108+
.addArguments("-TimeoutSeconds", Long.toString(Duration.ofSeconds(5).getSeconds()))
109+
.addArgument("-Executable").addArgument(cmd.appLauncherPath(launcherName))
110+
.dumpOutput());
111+
});
112+
113+
TKit.waitForFileCreated(traceFile, Duration.ofSeconds(20), Duration.ofSeconds(2));
114+
115+
try {
116+
TKit.waitForFileCreated(outputFile, Duration.ofSeconds(TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS * 2), Duration.ofSeconds(2));
117+
} finally {
118+
TKit.traceFileContents(traceFile, "Test app trace");
119+
}
120+
121+
TKit.assertFileExists(outputFile);
122+
return Files.readString(outputFile);
123+
}
124+
125+
private static final long TETS_APP_AUTOCLOSE_TIMEOUT_SECONDS = 30;
126+
127+
private static final Path TEST_APP_JAVA = TKit.TEST_SRC_ROOT.resolve("apps/UseShutdownHook.java");
128+
private static final Path TEST_PS1 = TKit.TEST_SRC_ROOT.resolve(Path.of("resources/Win8365790Test.ps1")).normalize();
129+
}

0 commit comments

Comments
 (0)