@@ -134,6 +134,14 @@ public int startServer() throws Exception {
134134 ProcessBuilder processBuilder = new ProcessBuilder (command );
135135 processBuilder .redirectErrorStream (true ); // Redirect standard error to the input stream
136136 runningProcess = processBuilder .start ();
137+
138+ // Add shutdown hook to ensure server is closed when Java app exits
139+ try {
140+ Runtime .getRuntime ().addShutdownHook (shutdownHook );
141+ } catch (IllegalStateException ignored ) {
142+ // JVM is already shutting down
143+ }
144+
137145 Thread processThread = new Thread (() -> {
138146 try (BufferedReader reader = new BufferedReader (new InputStreamReader (runningProcess .getInputStream ()))) {
139147 String line ;
@@ -145,11 +153,35 @@ public int startServer() throws Exception {
145153 e .printStackTrace ();
146154 }
147155 });
156+ runningProcess .onExit ().thenAccept (p -> {
157+ int code = p .exitValue ();
158+ System .err .println ("[BLEGrpcServer] exited: code=" + code +
159+ (code >= 128 ? " (likely signal " + (code - 128 ) + ")" : "" ));
160+ });
148161
149162 processThread .start ();
150163 return port ;
151164 // You can continue with other tasks here
152165 }
166+
167+ private final Thread shutdownHook = new Thread (() -> {
168+ Process p = runningProcess ;
169+ if (p == null ) return ;
170+
171+ try {
172+ // Try graceful termination first
173+ p .destroy (); // sends SIGTERM on Unix, WM_CLOSE/CTRL_BREAK semantics vary on Windows
174+ if (!p .waitFor (3 , java .util .concurrent .TimeUnit .SECONDS )) {
175+ // Fall back to force kill
176+ p .destroyForcibly ();
177+ p .waitFor (2 , java .util .concurrent .TimeUnit .SECONDS );
178+ }
179+ } catch (InterruptedException ie ) {
180+ Thread .currentThread ().interrupt ();
181+ } catch (Exception e ) {
182+ e .printStackTrace ();
183+ }
184+ });
153185
154186 public void stopServer () {
155187 if (runningProcess != null ) {
0 commit comments