diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcher.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcher.java index d91c544dc43..7fa71dfbaa0 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcher.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcher.java @@ -26,9 +26,11 @@ import lombok.extern.slf4j.Slf4j; import org.eclipse.lsp4j.services.LanguageServer; import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import javax.annotation.CheckForNull; +import java.util.Optional; + /** * Наблюдатель за жизнью родительского процесса, запустившего Language Server. */ @@ -38,7 +40,6 @@ public class ParentProcessWatcher { private final LanguageServer languageServer; - private long parentProcessId; /** * Обработчик события {@link LanguageServerInitializeRequestReceivedEvent}. @@ -49,29 +50,23 @@ public class ParentProcessWatcher { */ @EventListener public void handleEvent(LanguageServerInitializeRequestReceivedEvent event) { - var processId = event.getParams().getProcessId(); + @CheckForNull Integer processId = event.getParams().getProcessId(); if (processId == null) { return; } - parentProcessId = processId; - } - /** - * Фоновая процедура, отслеживающая родительский процесс. - */ - @Scheduled(fixedDelay = 30000L) - public void watch() { - if (parentProcessId == 0) { + // Can't register onExit callback on current process. + if (ProcessHandle.current().pid() == processId) { return; } - boolean processIsAlive = ProcessHandle.of(parentProcessId) - .map(ProcessHandle::isAlive) - .orElse(false); - - if (!processIsAlive) { - LOGGER.error("Parent process with pid {} is not found. Closing application...", parentProcessId); - languageServer.exit(); - } + Optional.of(processId) + .flatMap(ProcessHandle::of) + .map(ProcessHandle::onExit) + .ifPresent(onExitCallback -> onExitCallback.thenRun(() -> { + LOGGER.warn("Parent process with pid {} is not found. Closing application...", processId); + languageServer.exit(); + })); } + } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcherTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcherTest.java index 11d7c3a0235..77f45fb3dfb 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcherTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/ParentProcessWatcherTest.java @@ -25,53 +25,63 @@ import org.eclipse.lsp4j.InitializeParams; import org.eclipse.lsp4j.services.LanguageServer; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.springframework.boot.test.context.SpringBootTest; +import java.io.IOException; +import java.time.Duration; + +import static org.awaitility.Awaitility.await; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -@SpringBootTest class ParentProcessWatcherTest { - @InjectMocks - private ParentProcessWatcher parentProcessWatcher; - - @Mock - private LanguageServer languageServer; - @Test - void testParentProcessIsDead() { + void testParentProcessIsDead() throws IOException, InterruptedException { // given + var languageServer = mock(LanguageServer.class); + var parentProcessWatcher = new ParentProcessWatcher(languageServer); + var params = new InitializeParams(); - params.setProcessId(-1); + var process = new ProcessBuilder("timeout", "2").start(); + var pid = process.pid(); + params.setProcessId((int) pid); var event = new LanguageServerInitializeRequestReceivedEvent(languageServer, params); parentProcessWatcher.handleEvent(event); // when - parentProcessWatcher.watch(); + process.waitFor(); // then - verify(languageServer, times(1)).exit(); + await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted( + () -> verify(languageServer, times(1)).exit() + ); } @Test void testParentProcessIsAlive() { // given + var languageServer = mock(LanguageServer.class); + var parentProcessWatcher = new ParentProcessWatcher(languageServer); + var params = new InitializeParams(); params.setProcessId((int) ProcessHandle.current().pid()); var event = new LanguageServerInitializeRequestReceivedEvent(languageServer, params); - parentProcessWatcher.handleEvent(event); // when - parentProcessWatcher.watch(); + parentProcessWatcher.handleEvent(event); // then - verify(languageServer, never()).exit(); + await() + .during(Duration.ofSeconds(1)) + .untilAsserted( + () -> verify(languageServer, never()).exit() + ); } } \ No newline at end of file