2929import java .util .concurrent .CountDownLatch ;
3030import java .util .concurrent .ExecutorService ;
3131import java .util .concurrent .Executors ;
32+ import java .util .concurrent .TimeUnit ;
3233import java .util .concurrent .atomic .AtomicBoolean ;
3334
3435import org .eclipse .rdf4j .model .IRI ;
@@ -95,6 +96,11 @@ void gracefullyStopsOnSigterm() throws Exception {
9596 assertGracefulShutdown ("TERM" );
9697 }
9798
99+ @ Test
100+ void exitsOnSigtermBeforeContextAttachment () throws Exception {
101+ assertShutdownBeforeContextAttachment ("TERM" );
102+ }
103+
98104 private void assertGracefulShutdownWithSigintFallback () throws Exception {
99105 assertGracefulShutdown ("INT" , true );
100106 }
@@ -103,6 +109,52 @@ private void assertGracefulShutdown(String signalName) throws Exception {
103109 assertGracefulShutdown (signalName , false );
104110 }
105111
112+ private void assertShutdownBeforeContextAttachment (String signalName ) throws Exception {
113+ Path projectRoot = Path .of ("" ).toAbsolutePath ();
114+ String javaBin = Path .of (System .getProperty ("java.home" ), "bin" , "java" ).toString ();
115+ int serverPort = findFreePort ();
116+ int managementPort = findFreePort ();
117+
118+ Path targetDir = projectRoot .resolve ("target" );
119+ Path jarPath = Files .list (targetDir )
120+ .sorted (Comparator .comparing (Path ::toString ))
121+ .filter (p -> p .toString ().endsWith (".jar" ))
122+ .filter (p -> !p .toString ().endsWith ("-sources.jar" ))
123+ .filter (p -> !p .toString ().endsWith ("-javadoc.jar" ))
124+ .findFirst ()
125+ .orElseThrow (() -> new IllegalStateException ("Could not find executable JAR in " + targetDir ));
126+
127+ ProcessBuilder processBuilder = new ProcessBuilder (javaBin , "-jar" , jarPath .toString (),
128+ "--server.port=" + serverPort ,
129+ "--management.server.port=" + managementPort );
130+ processBuilder .directory (projectRoot .toFile ());
131+ processBuilder .redirectErrorStream (true );
132+
133+ Process process = processBuilder .start ();
134+ cleanupActions .add (() -> process .destroyForcibly ());
135+
136+ CountDownLatch started = new CountDownLatch (1 );
137+ StringBuilder outputBuffer = new StringBuilder ();
138+ startStreamGobbler (process , started , outputBuffer );
139+
140+ boolean registeredHandler = waitForOutputContains (outputBuffer ,
141+ "Registered SIGTERM handler for graceful shutdown." , 30 , SECONDS );
142+ assertThat (registeredHandler )
143+ .as (() -> "Did not observe SIGTERM handler registration before sending signal. Output:\\ n"
144+ + outputBuffer )
145+ .isTrue ();
146+
147+ sendSignal (process .pid (), signalName );
148+ boolean exited = process .waitFor (30 , SECONDS );
149+ assertThat (exited )
150+ .as (() -> "Process did not exit after SIG" + signalName
151+ + " sent before context attachment. Output:\\ n" + outputBuffer )
152+ .isTrue ();
153+ assertThat (process .exitValue ())
154+ .as (() -> "Process exit value after startup-phase SIG" + signalName + ". Output:\\ n" + outputBuffer )
155+ .isEqualTo (0 );
156+ }
157+
106158 private void assertGracefulShutdown (String signalName , boolean allowSigtermFallback ) throws Exception {
107159 Path projectRoot = Path .of ("" ).toAbsolutePath ();
108160 String javaBin = Path .of (System .getProperty ("java.home" ), "bin" , "java" ).toString ();
@@ -196,6 +248,22 @@ private void sendSignal(long pid, String signalName) throws IOException, Interru
196248 }
197249 }
198250
251+ private boolean waitForOutputContains (StringBuilder outputBuffer , String marker , long timeout , TimeUnit unit )
252+ throws InterruptedException {
253+ long deadline = System .nanoTime () + unit .toNanos (timeout );
254+ while (System .nanoTime () < deadline ) {
255+ synchronized (outputBuffer ) {
256+ if (outputBuffer .indexOf (marker ) >= 0 ) {
257+ return true ;
258+ }
259+ }
260+ Thread .sleep (100 );
261+ }
262+ synchronized (outputBuffer ) {
263+ return outputBuffer .indexOf (marker ) >= 0 ;
264+ }
265+ }
266+
199267 private void exerciseRemoteRepository (String serverUrl , StringBuilder outputBuffer )
200268 throws InterruptedException , RepositoryException , RepositoryConfigException {
201269 RemoteRepositoryManager manager = awaitRepositoryManager (serverUrl , outputBuffer );
0 commit comments